Decompiled source of EnemyDropLoot v1.2.0

EnemyDropLoot.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using DooDesch;
using EnemyDropLoot;
using EnemyDropLoot.Config;
using EnemyDropLoot.Managers;
using EnemyDropLoot.Services;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using MimicAPI.GameAPI;
using ReluProtocol;
using ReluProtocol.Enum;
using Steamworks;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "EnemyDropLoot", "1.2.0", "DooDesch", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: MelonOptionalDependencies(new string[] { "MimicAPI" })]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DooDesch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © DooDesch")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+78af3d3194119c25cf20a513c331c0fa91be1585")]
[assembly: AssemblyProduct("EnemyDropLoot")]
[assembly: AssemblyTitle("EnemyDropLoot")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.0.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 DooDesch
{
	internal sealed class HostSettings
	{
		[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyCreated")]
		private static class CreatedPatch
		{
			private static void Postfix(LobbyCreated_t callback)
			{
				//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_0007: Invalid comparison between Unknown and I4
				//IL_001e: 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)
				if ((int)callback.m_eResult != 1)
				{
					return;
				}
				foreach (HostSettings instance in Instances)
				{
					instance.Begin(new CSteamID(callback.m_ulSteamIDLobby), host: true);
				}
			}
		}

		[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyEntered")]
		private static class EnteredPatch
		{
			private static void Prefix(LobbyEnter_t callback)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: 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_0035: Unknown result type (might be due to invalid IL or missing references)
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				if (callback.m_EChatRoomEnterResponse != 1)
				{
					return;
				}
				CSteamID val = default(CSteamID);
				((CSteamID)(ref val))..ctor(callback.m_ulSteamIDLobby);
				foreach (HostSettings instance in Instances)
				{
					if (!instance.isHost || !(instance.lobby == val))
					{
						instance.Begin(val, host: false);
					}
				}
			}
		}

		[HarmonyPatch(typeof(SteamMatchmaking), "LeaveLobby")]
		private static class LeavePatch
		{
			private static void Prefix(CSteamID steamIDLobby)
			{
				//IL_0016: 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)
				foreach (HostSettings instance in Instances)
				{
					if (instance.lobby == steamIDLobby)
					{
						instance.Reset();
					}
				}
			}
		}

		[HarmonyPatch(typeof(MainMenu), "Start")]
		private static class MenuPatch
		{
			private static void Prefix()
			{
				foreach (HostSettings instance in Instances)
				{
					instance.Reset();
				}
			}
		}

		private static readonly List<HostSettings> Instances = new List<HostSettings>();

		private static Callback<LobbyDataUpdate_t> dataCallback;

		private static float nextPoll;

		private readonly string modId;

		private readonly string lobbyKey;

		private readonly int schemaVersion;

		private readonly List<ISetting> settings = new List<ISetting>();

		private readonly string modernKey;

		private string modernPayload;

		private string lastModernPayload;

		private long modernRevision;

		private bool legacyHost;

		private Dictionary<string, object> snapshot;

		private CSteamID lobby;

		private CSteamID owner;

		private bool isHost;

		private bool hostCreated;

		private bool authorityValid;

		private bool ownerChanged;

		private bool published;

		private string hostPayload;

		private string warningReason;

		private int warningRepeats;

		internal bool IsInSession => lobby != CSteamID.Nil;

		internal bool IsHost
		{
			get
			{
				if (isHost)
				{
					return authorityValid;
				}
				return false;
			}
		}

		internal bool UsingLegacyHost
		{
			get
			{
				if (!isHost && authorityValid && legacyHost)
				{
					return modernRevision == 0;
				}
				return false;
			}
		}

		internal event Action EffectiveValuesChanged;

		internal event Action LiveValuesChanged;

		internal HostSettings(string modId, int schemaVersion = 1)
		{
			if (string.IsNullOrWhiteSpace(modId))
			{
				throw new ArgumentException("A mod ID is required.", "modId");
			}
			if (schemaVersion < 1)
			{
				throw new ArgumentOutOfRangeException("schemaVersion");
			}
			this.modId = modId;
			this.schemaVersion = schemaVersion;
			lobbyKey = "doodesch." + modId.ToLowerInvariant() + ".settings.v" + schemaVersion;
			modernKey = lobbyKey + ".extended";
			if (Encoding.UTF8.GetByteCount(modernKey) >= 255)
			{
				throw new ArgumentException("The mod ID exceeds the Steam lobby key limit.", "modId");
			}
			foreach (HostSettings instance in Instances)
			{
				if (instance.lobbyKey == lobbyKey)
				{
					throw new ArgumentException("The mod already has a settings snapshot.", "modId");
				}
			}
			Instances.Add(this);
		}

		internal HostSetting<T> Register<T>(MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate = null, bool live = false, bool legacy = true, Func<T> localValue = null)
		{
			//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)
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			if (string.IsNullOrWhiteSpace(((MelonPreferences_Entry)entry).Identifier))
			{
				throw new ArgumentException("A setting identifier is required.", "entry");
			}
			if (((MelonPreferences_Entry)entry).Identifier.IndexOfAny(new char[3] { '\n', '\r', '\t' }) >= 0)
			{
				throw new ArgumentException("Setting identifiers cannot contain wire delimiters.", "entry");
			}
			if (lobby != CSteamID.Nil)
			{
				throw new InvalidOperationException("Register settings before entering a lobby.");
			}
			if (typeof(T) != typeof(bool) && typeof(T) != typeof(int) && typeof(T) != typeof(float) && typeof(T) != typeof(string))
			{
				throw new NotSupportedException("Host settings support bool, int, float and string values.");
			}
			if (legacy && typeof(T) == typeof(string))
			{
				throw new ArgumentException("String settings must use the extended snapshot.", "legacy");
			}
			foreach (ISetting setting in settings)
			{
				if (setting.Key == ((MelonPreferences_Entry)entry).Identifier)
				{
					throw new ArgumentException("Duplicate setting: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
				}
			}
			HostSetting<T> hostSetting = new HostSetting<T>(this, entry, vanillaValue, validate, live, legacy, localValue);
			if (!hostSetting.IsValid(vanillaValue))
			{
				throw new ArgumentException("The vanilla value is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "vanillaValue");
			}
			if (!hostSetting.IsValid(entry.DefaultValue))
			{
				throw new ArgumentException("The configured default is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
			}
			settings.Add(hostSetting);
			return hostSetting;
		}

		internal T GetValue<T>(string key, T localValue, T vanillaValue, bool live)
		{
			//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)
			if (lobby == CSteamID.Nil)
			{
				return localValue;
			}
			if (live && !authorityValid)
			{
				return vanillaValue;
			}
			if (snapshot == null || !snapshot.TryGetValue(key, out object value))
			{
				return vanillaValue;
			}
			return (T)value;
		}

		internal static void Update()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (Time.realtimeSinceStartup < nextPoll)
			{
				return;
			}
			nextPoll = Time.realtimeSinceStartup + 1f;
			foreach (HostSettings instance in Instances)
			{
				if (instance.lobby != CSteamID.Nil)
				{
					instance.Refresh();
				}
			}
		}

		private void Begin(CSteamID newLobby, bool host)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (settings.Count == 0 || (lobby == newLobby && (!host || hostCreated)))
			{
				return;
			}
			if (lobby != newLobby)
			{
				Reset();
			}
			lobby = newLobby;
			isHost = false;
			hostCreated = host;
			authorityValid = false;
			ownerChanged = false;
			snapshot = null;
			published = false;
			hostPayload = null;
			owner = CSteamID.Nil;
			try
			{
				owner = SteamMatchmaking.GetLobbyOwner(lobby);
				CSteamID steamID = SteamUser.GetSteamID();
				if (owner == CSteamID.Nil || (host ? (owner != steamID) : (owner == steamID)))
				{
					owner = CSteamID.Nil;
					Warn("Lobby owner does not identify the expected host; using vanilla settings.");
					Changed();
					return;
				}
				isHost = host;
				authorityValid = true;
				if (host)
				{
					CaptureHostSnapshot();
				}
				else
				{
					CaptureClientSnapshot();
				}
			}
			catch (Exception ex)
			{
				Warn("Initial lobby settings failed; vanilla settings are frozen for this session: " + ex.GetType().Name + ": " + ex.Message);
			}
			Refresh();
			Changed();
		}

		private static void EnsureCallback()
		{
			if (dataCallback == null)
			{
				dataCallback = Callback<LobbyDataUpdate_t>.Create((DispatchDelegate<LobbyDataUpdate_t>)OnLobbyDataUpdated);
			}
		}

		private static void OnLobbyDataUpdated(LobbyDataUpdate_t callback)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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)
			foreach (HostSettings instance in Instances)
			{
				if (instance.lobby.m_SteamID == callback.m_ulSteamIDLobby && callback.m_ulSteamIDMember == callback.m_ulSteamIDLobby)
				{
					if (callback.m_bSuccess == 0)
					{
						instance.Warn("Steam lobby data update failed; frozen session settings are retained.");
					}
					else
					{
						instance.Refresh();
					}
				}
			}
		}

		private void CaptureHostSnapshot()
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
			StringBuilder stringBuilder = new StringBuilder(schemaVersion.ToString(CultureInfo.InvariantCulture));
			for (int i = 0; i < settings.Count; i++)
			{
				ISetting setting = settings[i];
				object localValue = setting.LocalValue;
				dictionary.Add(setting.Key, localValue);
				if (setting.Legacy)
				{
					stringBuilder.Append('\n').Append(setting.Key).Append('\t')
						.Append(Format(localValue));
				}
			}
			string s = stringBuilder.ToString();
			if (Encoding.UTF8.GetByteCount(s) + 1 > 8192)
			{
				throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
			}
			hostPayload = s;
			snapshot = dictionary;
			modernRevision = 1L;
			modernPayload = BuildModern(dictionary, modernRevision);
		}

		private unsafe void CaptureClientSnapshot()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
			if (!string.IsNullOrEmpty(lobbyData))
			{
				snapshot = ParseModern(lobbyData, out modernRevision);
				lastModernPayload = lobbyData;
				MelonLogger.Msg("[" + modId + "] Host settings captured; live values follow the host, lobby values remain fixed.");
				return;
			}
			string lobbyData2 = SteamMatchmaking.GetLobbyData(lobby, lobbyKey);
			if (string.IsNullOrEmpty(lobbyData2))
			{
				Warn("Host snapshot is absent; vanilla settings are frozen for this session.");
				return;
			}
			snapshot = ParseSnapshot(lobbyData2);
			legacyHost = true;
			string[] obj = new string[5] { "[", modId, "] Host settings snapshot frozen for lobby ", null, null };
			CSteamID val = lobby;
			obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
			obj[4] = ".";
			MelonLogger.Msg(string.Concat(obj));
		}

		private void Refresh()
		{
			//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_0042: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_002d: 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)
			bool flag = false;
			try
			{
				EnsureCallback();
				CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(lobby);
				if (ownerChanged || owner == CSteamID.Nil || lobbyOwner != owner)
				{
					authorityValid = false;
					if (lobbyOwner != CSteamID.Nil && lobbyOwner != owner)
					{
						ownerChanged = true;
					}
					Warn("Lobby owner changed or is unavailable; authority revoked and frozen session settings retained.");
					return;
				}
				bool num = !authorityValid;
				authorityValid = true;
				flag = true;
				if (num)
				{
					MelonLogger.Msg("[" + modId + "] Pinned lobby authority recovered; frozen settings retained.");
				}
				if (isHost)
				{
					if (hostPayload != null)
					{
						if (published)
						{
							RefreshHostLive();
						}
						else
						{
							Publish();
						}
					}
				}
				else
				{
					RefreshClientLive();
				}
			}
			catch (Exception ex)
			{
				authorityValid = flag;
				Warn("Refresh lobby settings failed; frozen session settings retained: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private unsafe void Publish()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload && !SteamMatchmaking.SetLobbyData(lobby, lobbyKey, hostPayload))
			{
				CSteamID val = lobby;
				Warn("SetLobbyData rejected the settings snapshot for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
			}
			else if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload)
			{
				CSteamID val = lobby;
				Warn("Settings snapshot readback differs for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
			}
			else if (PublishModern(modernPayload))
			{
				published = true;
				Recover();
				string[] obj = new string[5] { "[", modId, "] Settings snapshot confirmed by local lobby readback for ", null, null };
				CSteamID val = lobby;
				obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
				obj[4] = ".";
				MelonLogger.Msg(string.Concat(obj));
			}
		}

		private Dictionary<string, object> ParseSnapshot(string payload)
		{
			if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
			{
				throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
			}
			string[] array = payload.Split('\n');
			if (array[0] != schemaVersion.ToString(CultureInfo.InvariantCulture))
			{
				throw new FormatException("Settings snapshot schema mismatch.");
			}
			if (array.Length != settings.FindAll((ISetting s) => s.Legacy).Count + 1)
			{
				throw new FormatException("Settings snapshot has missing or extra keys.");
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
			for (int num = 1; num < array.Length; num++)
			{
				string text = array[num];
				int num2 = text.IndexOf('\t');
				if (num2 <= 0 || num2 != text.LastIndexOf('\t') || text.IndexOf('\r') >= 0)
				{
					throw new FormatException("Settings snapshot has an invalid entry delimiter.");
				}
				string key = text.Substring(0, num2);
				if (dictionary.ContainsKey(key))
				{
					throw new FormatException("Settings snapshot has a duplicate or missing key.");
				}
				ISetting setting = settings.Find((ISetting candidate) => candidate.Key == key && candidate.Legacy);
				if (setting == null)
				{
					throw new FormatException("Settings snapshot has an unknown key: " + key);
				}
				dictionary.Add(key, setting.Parse(text.Substring(num2 + 1)));
			}
			return dictionary;
		}

		private string BuildModern(Dictionary<string, object> values, long revision)
		{
			StringBuilder stringBuilder = new StringBuilder("2\t" + revision.ToString(CultureInfo.InvariantCulture));
			foreach (ISetting setting in settings)
			{
				stringBuilder.Append('\n').Append(setting.Key).Append('\t')
					.Append(Format(values[setting.Key]));
			}
			string text = stringBuilder.ToString();
			if (Encoding.UTF8.GetByteCount(text) + 1 > 8192)
			{
				throw new FormatException("Extended settings exceed the Steam metadata limit.");
			}
			return text;
		}

		private Dictionary<string, object> ParseModern(string payload, out long revision)
		{
			if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
			{
				throw new FormatException("Extended settings exceed the Steam metadata limit.");
			}
			string[] array = payload.Split('\n');
			string[] array2 = array[0].Split('\t');
			if (array2.Length != 2 || array2[0] != "2" || !long.TryParse(array2[1], NumberStyles.None, CultureInfo.InvariantCulture, out revision) || revision < 1)
			{
				throw new FormatException("Invalid extended settings schema or revision.");
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			for (int i = 1; i < array.Length; i++)
			{
				int num = array[i].IndexOf('\t');
				if (num <= 0 || num != array[i].LastIndexOf('\t') || array[i].IndexOf('\r') >= 0)
				{
					throw new FormatException("Invalid extended settings delimiter.");
				}
				string key = array[i].Substring(0, num);
				if (!hashSet.Add(key))
				{
					throw new FormatException("Duplicate extended setting: " + key);
				}
				ISetting setting = settings.Find((ISetting s) => s.Key == key);
				if (setting != null)
				{
					dictionary.Add(key, setting.Parse(array[i].Substring(num + 1)));
				}
			}
			return dictionary;
		}

		private bool PublishModern(string payload)
		{
			//IL_0001: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload && !SteamMatchmaking.SetLobbyData(lobby, modernKey, payload))
			{
				Warn("SetLobbyData rejected the extended settings; last confirmed values retained.");
				return false;
			}
			if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload)
			{
				Warn("Extended settings readback differs; last confirmed values retained.");
				return false;
			}
			return true;
		}

		private void RefreshHostLive()
		{
			if (snapshot == null)
			{
				return;
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(snapshot, StringComparer.Ordinal);
			bool flag = false;
			foreach (ISetting setting in settings)
			{
				if (setting.Live)
				{
					object localValue = setting.LocalValue;
					if (!dictionary.TryGetValue(setting.Key, out var value) || !object.Equals(value, localValue))
					{
						dictionary[setting.Key] = localValue;
						flag = true;
					}
				}
			}
			if (!flag)
			{
				if (PublishModern(modernPayload))
				{
					Recover();
				}
				return;
			}
			long revision = checked(modernRevision + 1);
			string payload = BuildModern(dictionary, revision);
			if (PublishModern(payload))
			{
				snapshot = dictionary;
				modernRevision = revision;
				modernPayload = payload;
				Recover();
				Changed(this.LiveValuesChanged);
			}
		}

		private void RefreshClientLive()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
			if (string.IsNullOrEmpty(lobbyData) || lobbyData == lastModernPayload)
			{
				return;
			}
			long revision;
			Dictionary<string, object> dictionary = ParseModern(lobbyData, out revision);
			if (revision <= modernRevision)
			{
				Warn("Extended settings revision did not advance; last confirmed values retained.");
				return;
			}
			Dictionary<string, object> dictionary2 = ((snapshot == null) ? new Dictionary<string, object>(StringComparer.Ordinal) : new Dictionary<string, object>(snapshot, StringComparer.Ordinal));
			bool flag = false;
			foreach (ISetting setting in settings)
			{
				if (setting.Live && dictionary.TryGetValue(setting.Key, out var value) && (!dictionary2.TryGetValue(setting.Key, out var value2) || !object.Equals(value2, value)))
				{
					dictionary2[setting.Key] = value;
					flag = true;
				}
			}
			snapshot = dictionary2;
			modernRevision = revision;
			lastModernPayload = lobbyData;
			Recover();
			if (flag)
			{
				Changed(this.LiveValuesChanged);
			}
		}

		private static string Format(object value)
		{
			if (value is string s)
			{
				return "s:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(s));
			}
			if (value is float num)
			{
				return num.ToString("R", CultureInfo.InvariantCulture);
			}
			if (value is bool)
			{
				if (!(bool)value)
				{
					return "false";
				}
				return "true";
			}
			return ((int)value).ToString(CultureInfo.InvariantCulture);
		}

		private void Warn(string reason)
		{
			if (warningReason == reason)
			{
				warningRepeats++;
				return;
			}
			if (warningRepeats > 0)
			{
				MelonLogger.Warning("[" + modId + "] Previous settings warning repeated " + warningRepeats + " times.");
			}
			warningReason = reason;
			warningRepeats = 0;
			MelonLogger.Warning("[" + modId + "] " + reason);
		}

		private void Recover()
		{
			if (warningReason != null)
			{
				MelonLogger.Msg("[" + modId + "] Lobby settings recovered after " + (warningRepeats + 1) + " warning occurrences.");
				warningReason = null;
				warningRepeats = 0;
			}
		}

		private void Changed()
		{
			Changed(this.EffectiveValuesChanged);
		}

		private void Changed(Action handlers)
		{
			if (handlers == null)
			{
				return;
			}
			Delegate[] invocationList = handlers.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action = (Action)invocationList[i];
				try
				{
					action();
				}
				catch (Exception ex)
				{
					MelonLogger.Error("[" + modId + "] Apply effective settings failed.", ex);
				}
			}
		}

		private void Reset()
		{
			//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_0011: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			bool num = lobby != CSteamID.Nil;
			lobby = CSteamID.Nil;
			owner = CSteamID.Nil;
			isHost = false;
			hostCreated = false;
			authorityValid = false;
			ownerChanged = false;
			published = false;
			snapshot = null;
			hostPayload = null;
			modernPayload = null;
			lastModernPayload = null;
			modernRevision = 0L;
			legacyHost = false;
			warningReason = null;
			warningRepeats = 0;
			if (num)
			{
				MelonLogger.Msg("[" + modId + "] Session settings cleared; local settings restored.");
				Changed();
			}
		}
	}
	internal interface ISetting
	{
		string Key { get; }

		object LocalValue { get; }

		bool Live { get; }

		bool Legacy { get; }

		bool IsValid(object value);

		object Parse(string value);
	}
	internal sealed class HostSetting<T> : ISetting
	{
		private readonly HostSettings owner;

		private readonly MelonPreferences_Entry<T> entry;

		private readonly T vanillaValue;

		private readonly Func<T, bool> validate;

		private readonly Func<T> localValue;

		private bool invalidLocal;

		public bool Live { get; }

		public bool Legacy { get; }

		internal T Value => owner.GetValue(Key, (T)LocalValue, vanillaValue, Live);

		public string Key => ((MelonPreferences_Entry)entry).Identifier;

		public object LocalValue
		{
			get
			{
				T val = ((localValue == null) ? entry.Value : localValue());
				if (IsValid(val))
				{
					if (invalidLocal)
					{
						MelonLogger.Msg("Local setting recovered: " + Key);
					}
					invalidLocal = false;
					return val;
				}
				if (!invalidLocal)
				{
					MelonLogger.Warning("Invalid local setting " + Key + "; using its configured default without overwriting the file.");
				}
				invalidLocal = true;
				return entry.DefaultValue;
			}
		}

		internal HostSetting(HostSettings owner, MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate, bool live, bool legacy, Func<T> localValue)
		{
			this.owner = owner;
			this.entry = entry;
			this.vanillaValue = vanillaValue;
			this.validate = validate;
			this.localValue = localValue;
			Live = live;
			Legacy = legacy;
		}

		public bool IsValid(object value)
		{
			if (!(value is T arg))
			{
				return false;
			}
			if (value is float f && (float.IsNaN(f) || float.IsInfinity(f)))
			{
				return false;
			}
			if (value is string text && (Encoding.UTF8.GetByteCount(text) > 1024 || text.IndexOf('\0') >= 0))
			{
				return false;
			}
			if (validate != null)
			{
				return validate(arg);
			}
			return true;
		}

		public object Parse(string value)
		{
			object obj;
			int result2;
			float result3;
			if (typeof(T) == typeof(bool) && bool.TryParse(value, out var result))
			{
				obj = result;
			}
			else if (typeof(T) == typeof(int) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
			{
				obj = result2;
			}
			else if (typeof(T) == typeof(float) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result3))
			{
				obj = result3;
			}
			else
			{
				if (!(typeof(T) == typeof(string)) || !value.StartsWith("s:", StringComparison.Ordinal))
				{
					throw new FormatException("Invalid settings value: " + Key);
				}
				obj = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(value.Substring(2)));
			}
			if (!IsValid(obj))
			{
				throw new FormatException("Settings value is out of range: " + Key);
			}
			return obj;
		}
	}
	internal static class ModVersion
	{
		internal const string Current = "1.2.0";
	}
}
namespace EnemyDropLoot
{
	public sealed class Core : MelonMod
	{
		public override void OnInitializeMelon()
		{
			EnemyDropLootPreferences.Initialize();
			MelonLogger.Msg($"EnemyDropLoot initialized. Enabled={EnemyDropLootPreferences.Enabled}, DropChance={EnemyDropLootPreferences.DropChance:P0}, MaxDropsPerKill={EnemyDropLootPreferences.MaxDropsPerKill}");
		}

		public override void OnUpdate()
		{
			HostSettings.Update();
		}
	}
}
namespace EnemyDropLoot.Services
{
	internal static class LootSpawnService
	{
		internal static bool TrySpawnLoot(VMonster monster, int itemMasterId)
		{
			//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_0033: 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_003f: 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_0062: 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_00cd: 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)
			VWorld vWorld = CoreAPI.GetVWorld();
			if (vWorld == null)
			{
				MelonLogger.Warning($"EnemyDropLoot: Skipping loot item {itemMasterId} for monster {((VActor)monster).MasterID}: world is unavailable.");
				return false;
			}
			Vector3 positionVector = ((VActor)monster).PositionVector;
			Vector3 val = vWorld.FindNearestPoly(positionVector, 100f);
			if (val == NavMeshConstants.INVALID_POSITION)
			{
				MelonLogger.Warning($"EnemyDropLoot: Skipping loot item {itemMasterId} for monster {((VActor)monster).MasterID}: no valid navmesh position near {positionVector}.");
				return false;
			}
			ItemElement newItemElement = ((VActor)monster).VRoom.GetNewItemElement(itemMasterId, false, 1, 0, 0, 0);
			if (newItemElement == null)
			{
				MelonLogger.Warning($"EnemyDropLoot: Failed to create loot item {itemMasterId} for monster {((VActor)monster).MasterID}.");
				return false;
			}
			PosWithRot val2 = ((VActor)monster).Position.Clone();
			val2.x = val.x;
			val2.y = val.y;
			val2.z = val.z;
			if (((VActor)monster).VRoom.SpawnLootingObject(newItemElement, val2, ((VActor)monster).IsIndoor, (ReasonOfSpawn)4, 0, 0, 0L, false, false) == 0)
			{
				MelonLogger.Warning($"EnemyDropLoot: Failed to spawn loot item {itemMasterId} for monster {((VActor)monster).MasterID}.");
				return false;
			}
			return true;
		}
	}
}
namespace EnemyDropLoot.Patches
{
	[HarmonyPatch(typeof(DungeonRoom), "Initialize")]
	internal static class DungeonRoomInitializePatch
	{
		private static void Postfix(DungeonRoom __instance)
		{
			LootPoolManager.ConfigureForDungeon(__instance);
		}
	}
	[HarmonyPatch(typeof(IVroom), "OnVacateRoom", new Type[] { typeof(bool) })]
	internal static class DungeonRoomVacatePatch
	{
		private static void Postfix(IVroom __instance)
		{
			if (__instance is DungeonRoom)
			{
				LootPoolManager.Reset();
			}
		}
	}
	[HarmonyPatch(typeof(InventoryController), "OnDead")]
	internal static class FakeItemDeathDropPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(InventoryController), "ExtractAllItemElement", new Type[1] { typeof(bool) }, (Type[])null);
			MethodInfo operand = AccessTools.Method(typeof(FakeItemDeathDropPatch), "ExtractDeathDrops", (Type[])null, (Type[])null);
			List<CodeInstruction> list = instructions.ToList();
			int num = 0;
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Calls(item, methodInfo))
				{
					item.opcode = OpCodes.Call;
					item.operand = operand;
					num++;
				}
			}
			if (num != 1)
			{
				throw new InvalidOperationException($"EnemyDropLoot: InventoryController.OnDead must extract inventory exactly once; found {num} calls.");
			}
			return list;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static Dictionary<int, ItemElement> ExtractDeathDrops(InventoryController inventory, bool turnOff)
		{
			Dictionary<int, ItemElement> dictionary = inventory.ExtractAllItemElement(turnOff);
			if (!EnemyDropLootPreferences.RemoveFakeItemDrops)
			{
				return dictionary;
			}
			int[] array = (from item in dictionary
				where item.Value.IsFake
				select item.Key).ToArray();
			foreach (int key in array)
			{
				dictionary.Remove(key);
			}
			return dictionary;
		}
	}
	[HarmonyPatch(typeof(VMonster), "OnDying")]
	internal static class VMonsterOnDyingPatch
	{
		private static void Postfix(VMonster __instance)
		{
			LootPoolManager.TryHandleMonsterDeath(__instance);
		}
	}
}
namespace EnemyDropLoot.Managers
{
	internal static class LootPoolManager
	{
		private static readonly List<RandomSpawnedItemActorData> LootSources = new List<RandomSpawnedItemActorData>();

		private static readonly List<int> FallbackItemIds = new List<int>();

		private static IVroom _activeRoom = null;

		private static bool _hasActiveRoom;

		private static string _allowText;

		private static string _blockText;

		private static HashSet<int> _allowed = new HashSet<int>();

		private static HashSet<int> _blocked = new HashSet<int>();

		private static bool _emptyFilterReported;

		internal static void Reset()
		{
			LootSources.Clear();
			FallbackItemIds.Clear();
			_hasActiveRoom = false;
			_emptyFilterReported = false;
		}

		internal static void ConfigureForDungeon(DungeonRoom room)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Invalid comparison between Unknown and I4
			Reset();
			_activeRoom = (IVroom)(object)room;
			_hasActiveRoom = true;
			try
			{
				Dictionary<int, SpawnedActorData> fieldValue = ReflectionHelper.GetFieldValue<Dictionary<int, SpawnedActorData>>((object)room, "_spawnedActorDatas");
				if (fieldValue == null)
				{
					MelonLogger.Warning("EnemyDropLoot: Unable to fetch spawn data for current dungeon.");
					return;
				}
				foreach (SpawnedActorData value in fieldValue.Values)
				{
					if (value == null || (int)value.MarkerType != 1)
					{
						continue;
					}
					RandomSpawnedItemActorData val = (RandomSpawnedItemActorData)(object)((value is RandomSpawnedItemActorData) ? value : null);
					if (val == null)
					{
						continue;
					}
					LootSources.Add(val);
					foreach (KeyValuePair<int, (int, int)> candidate in val.Candidates)
					{
						if (!FallbackItemIds.Contains(candidate.Key))
						{
							FallbackItemIds.Add(candidate.Key);
						}
					}
				}
				MelonLogger.Msg("EnemyDropLoot: Loaded {0} loot spawn bundles with {1} unique items.", new object[2] { LootSources.Count, FallbackItemIds.Count });
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"EnemyDropLoot: Failed to read dungeon loot pool. {arg}");
			}
		}

		internal static void TryHandleMonsterDeath(VMonster monster)
		{
			if (!EnemyDropLootPreferences.Enabled || LootSources.Count == 0 || !_hasActiveRoom || ((VActor)monster).VRoom != _activeRoom)
			{
				return;
			}
			int num = EnemyDropLootPreferences.RollDropCount();
			if (num <= 0)
			{
				return;
			}
			for (int i = 0; i < num; i++)
			{
				if (!TryPickItem(out var itemMasterId))
				{
					break;
				}
				if (!LootSpawnService.TrySpawnLoot(monster, itemMasterId))
				{
					break;
				}
			}
		}

		private static bool TryPickItem(out int itemMasterId)
		{
			itemMasterId = 0;
			if (LootSources.Count == 0)
			{
				return false;
			}
			RefreshFilters();
			if (_allowed.Count != 0 || _blocked.Count != 0)
			{
				int[] array = FallbackItemIds.Where(IsAllowed).ToArray();
				if (array.Length == 0)
				{
					if (!_emptyFilterReported)
					{
						MelonLogger.Msg("EnemyDropLoot: No map loot matches the host's item filters; extra drops skipped.");
					}
					_emptyFilterReported = true;
					return false;
				}
				for (int i = 0; i < 32; i++)
				{
					int pickedItemValue = LootSources[Random.Range(0, LootSources.Count)].GetPickedItemValue();
					if (pickedItemValue != 0 && IsAllowed(pickedItemValue))
					{
						itemMasterId = pickedItemValue;
						return true;
					}
				}
				itemMasterId = array[Random.Range(0, array.Length)];
				return true;
			}
			int num = LootSources[Random.Range(0, LootSources.Count)].GetPickedItemValue();
			if (num == 0 && FallbackItemIds.Count > 0)
			{
				num = FallbackItemIds[Random.Range(0, FallbackItemIds.Count)];
			}
			if (num == 0)
			{
				return false;
			}
			itemMasterId = num;
			return true;
		}

		private static bool IsAllowed(int id)
		{
			if (_allowed.Count == 0 || _allowed.Contains(id))
			{
				return !_blocked.Contains(id);
			}
			return false;
		}

		private static void RefreshFilters()
		{
			string allowedItemIds = EnemyDropLootPreferences.AllowedItemIds;
			string blockedItemIds = EnemyDropLootPreferences.BlockedItemIds;
			if (!(allowedItemIds == _allowText) || !(blockedItemIds == _blockText))
			{
				_allowed = ParseIds(allowedItemIds);
				_blocked = ParseIds(blockedItemIds);
				_allowText = allowedItemIds;
				_blockText = blockedItemIds;
				_emptyFilterReported = false;
			}
		}

		private static HashSet<int> ParseIds(string text)
		{
			if (!string.IsNullOrWhiteSpace(text))
			{
				return new HashSet<int>(from id in text.Split(',')
					select int.Parse(id, NumberStyles.Integer, CultureInfo.InvariantCulture));
			}
			return new HashSet<int>();
		}
	}
}
namespace EnemyDropLoot.Config
{
	internal static class EnemyDropLootPreferences
	{
		private const string CategoryId = "EnemyDropLoot";

		private static MelonPreferences_Category _category;

		private static MelonPreferences_Entry<bool> _enabled;

		private static MelonPreferences_Entry<float> _dropChance;

		private static MelonPreferences_Entry<int> _maxDropsPerKill;

		private static MelonPreferences_Entry<bool> _removeFakeItemDrops;

		private static HostSettings _hostSettings;

		private static HostSetting<bool> _hostEnabled;

		private static HostSetting<float> _hostDropChance;

		private static HostSetting<int> _hostMaxDropsPerKill;

		private static HostSettings _fakeDropSettings;

		private static HostSetting<bool> _hostRemoveFakeItemDrops;

		private static HostSetting<string> _allowedItemIds;

		private static HostSetting<string> _blockedItemIds;

		internal static bool Enabled => _hostEnabled?.Value ?? false;

		internal static float DropChance => Mathf.Clamp01(_hostDropChance?.Value ?? 0f);

		internal static int MaxDropsPerKill => Mathf.Clamp(_hostMaxDropsPerKill?.Value ?? 0, 0, 100);

		internal static bool RemoveFakeItemDrops
		{
			get
			{
				if (Enabled)
				{
					HostSettings fakeDropSettings = _fakeDropSettings;
					if (fakeDropSettings != null && fakeDropSettings.IsHost)
					{
						return _hostRemoveFakeItemDrops?.Value ?? false;
					}
				}
				return false;
			}
		}

		internal static string AllowedItemIds => _allowedItemIds?.Value ?? "";

		internal static string BlockedItemIds => _blockedItemIds?.Value ?? "";

		internal static void Initialize()
		{
			if (_category == null)
			{
				_category = MelonPreferences.CreateCategory("EnemyDropLoot", "Enemy Drop Loot");
				_enabled = CreateEntry("Enabled", defaultValue: true, "Enabled", "Toggle the Enemy Drop Loot mod. Default: true");
				_dropChance = CreateEntry("DropChance", 0.1f, "Drop chance", "Chance (0-1) per roll that a slain enemy drops loot. Default: 0.1");
				_maxDropsPerKill = CreateEntry("MaxDropsPerKill", 1, "Max drops per kill", "Maximum number of loot rolls performed per enemy. Default: 1");
				_removeFakeItemDrops = CreateEntry("RemoveFakeItemDrops", defaultValue: false, "Remove fake item drops", "Prevent fake inventory items from dropping when a creature dies. Real items and extra loot are kept. Host setting. Default: false");
				_hostSettings = new HostSettings("EnemyDropLoot");
				_hostEnabled = _hostSettings.Register(_enabled, vanillaValue: false, null, live: true);
				_hostDropChance = _hostSettings.Register(_dropChance, 0f, (float value) => value >= 0f && value <= 1f, live: true, legacy: true, () => Mathf.Clamp01(_dropChance.Value));
				_hostMaxDropsPerKill = _hostSettings.Register(_maxDropsPerKill, 0, (int value) => value >= 0 && value <= 100, live: true, legacy: true, () => Mathf.Clamp(_maxDropsPerKill.Value, 0, 100));
				_allowedItemIds = _hostSettings.Register(CreateEntry("AllowedItemIds", "", "Allowed Item IDs", "Host setting, live for subsequent drops. Comma-separated item master IDs allowed from the current map pool. Empty allows all map items; blocked IDs take priority. Up to 128 IDs."), "", IsValidItemList, live: true, legacy: false);
				_blockedItemIds = _hostSettings.Register(CreateEntry("BlockedItemIds", "", "Blocked Item IDs", "Host setting, live for subsequent drops. Comma-separated item master IDs excluded from extra loot. Empty blocks none. Up to 128 IDs."), "", IsValidItemList, live: true, legacy: false);
				_fakeDropSettings = new HostSettings("EnemyDropLoot.FakeDrops");
				_hostRemoveFakeItemDrops = _fakeDropSettings.Register(_removeFakeItemDrops, vanillaValue: false, null, live: true);
			}
		}

		private static MelonPreferences_Entry<T> CreateEntry<T>(string identifier, T defaultValue, string displayName, string? description = null)
		{
			if (_category == null)
			{
				throw new InvalidOperationException("Preference category not initialized.");
			}
			return _category.CreateEntry<T>(identifier, defaultValue, displayName, description, false, false, (ValueValidator)null, (string)null);
		}

		private static bool IsValidItemList(string text)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				return true;
			}
			string[] array = text.Split(',');
			if (array.Length > 128)
			{
				return false;
			}
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				if (!int.TryParse(array2[i], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0)
				{
					return false;
				}
			}
			return true;
		}

		internal static int RollDropCount()
		{
			if (!Enabled)
			{
				return 0;
			}
			int maxDropsPerKill = MaxDropsPerKill;
			float dropChance = DropChance;
			if (maxDropsPerKill == 0 || dropChance <= 0f)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < maxDropsPerKill; i++)
			{
				if (dropChance >= 1f || Random.value < dropChance)
				{
					num++;
				}
			}
			return num;
		}
	}
}