Decompiled source of CatchupLoot v3.0.0

CatchupLoot.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
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 HG.Reflection;
using Microsoft.CodeAnalysis;
using On.RoR2;
using On.RoR2.Networking;
using R2API.Networking;
using R2API.Networking.Interfaces;
using RoR2;
using RoR2.Artifacts;
using RoR2.Networking;
using RoR2.UI;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: OptIn]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CatchupLoot")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+54de05d8639907e57a333aaccea732a1ae8f557e")]
[assembly: AssemblyProduct("CatchupLoot")]
[assembly: AssemblyTitle("CatchupLoot")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 CatchupLoot
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("zerocodehero.CatchupLoot", "CatchupLoot", "2.2.0")]
	public class CatchupLootPlugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_GetExpAdjustedDropChancePercent <0>__Util_GetExpAdjustedDropChancePercent;

			public static NetworkMessageDelegate <1>__HandlePickupMessage;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static hook_DropRewards <>9__40_0;

			public static Func<PlayerCharacterMasterController, bool> <>9__56_0;

			internal void <OnEnable>b__40_0(orig_DropRewards orig, BossGroup self)
			{
				if (EnablePlayerDropRateScaling.Value && IsModHooked)
				{
					self.scaleRewardsByPlayerCount = false;
				}
				orig.Invoke(self);
			}

			internal bool <GetPlayerCount>b__56_0(PlayerCharacterMasterController pc)
			{
				return pc.isConnected;
			}
		}

		public const string PluginGUID = "zerocodehero.CatchupLoot";

		public const string PluginAuthor = "zerocodehero";

		public const string PluginName = "CatchupLoot";

		public const string PluginVersion = "2.2.0";

		public static float dropChance = 5f;

		private static bool IsModHooked = false;

		private bool forceDisabled;

		private static bool useManualDropRate = false;

		private static DropRateManager dropRateManager;

		private static ConfigEntry<float> DropChanceMultiplier { get; set; }

		private static ConfigEntry<float> MinimumDropChance { get; set; }

		private static ConfigEntry<float> BaseDropChance { get; set; }

		private static ConfigEntry<bool> EnablePlayerDropRateScaling { get; set; }

		private static ConfigEntry<bool> EnableSwarmsScaling { get; set; }

		private static ConfigEntry<bool> EnableBadLuckProtection { get; set; }

		private static ConfigEntry<KeyCode> PullItemsHotKey { get; set; }

		public void Awake()
		{
			Log.Init(((BaseUnityPlugin)this).Logger);
			DropChanceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DropChanceMultiplier", 1f, "Manipulate drop rate. Use `0.5` to halve or `2` to double.");
			MinimumDropChance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MinimumDropChance", 1f, "The lowest possible drop chance when player count scaling is active.");
			EnablePlayerDropRateScaling = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnablePlayerBasedDropRateScaling", true, "Enabling this will reduce drop rates to account for shared loot.");
			EnableSwarmsScaling = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableSwarmsScaling", true, "Enabling this will (correctly) reduce drop rates for Artifact of Swarms.");
			EnableBadLuckProtection = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableBadLuckProtection", true, "Enabling this will increase drop rate consistency with low drop rates.");
			PullItemsHotKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "PullItemsHotkey", (KeyCode)284, "Hotkey to use to pull items to yourself.");
			BaseDropChance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BaseDropChance", 5f, "Base item drop chance. It is recommended to leave this at the default value.");
			dropChance = BaseDropChance.Value;
			dropRateManager = new DropRateManager(EnableBadLuckProtection, EnableSwarmsScaling, DropChanceMultiplier, BaseDropChance, MinimumDropChance);
			Log.Info("CatchupLoot loaded");
		}

		private void Update()
		{
			//IL_0005: 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_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_008c: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			if (!Input.GetKeyDown(PullItemsHotKey.Value) || !IsModHooked)
			{
				return;
			}
			try
			{
				Object.FindObjectsOfType<RuleBookViewer>();
				PickupDisplay[] array = Object.FindObjectsOfType<PickupDisplay>();
				Vector3 position = ((MPEventSystem)EventSystem.current).localUser.cachedMaster.GetBodyObject().transform.position;
				float num = 10f;
				Vector3 position2 = default(Vector3);
				for (int i = 0; i < array.Length; i++)
				{
					PickupDisplay val = array[i];
					if (((Object)val.highlight).name.StartsWith("CommandCube"))
					{
						float num2 = (float)i * MathF.PI * 2f / (float)array.Length;
						((Vector3)(ref position2))..ctor(position.x + Mathf.Cos(num2) * num, position.y, position.z + Mathf.Sin(num2) * num);
						((Component)val).gameObject.transform.position = position2;
					}
				}
			}
			catch (NullReferenceException)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)"Failed to pull CommandCubes with F3 - most likely because there were none.");
			}
		}

		private void OnApplicationFocus(bool hasFocus)
		{
			if (hasFocus)
			{
				ReevaluateLifecycle();
			}
		}

		private void OnEnable()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			object obj = <>c.<>9__40_0;
			if (obj == null)
			{
				hook_DropRewards val = delegate(orig_DropRewards orig, BossGroup self)
				{
					if (EnablePlayerDropRateScaling.Value && IsModHooked)
					{
						self.scaleRewardsByPlayerCount = false;
					}
					orig.Invoke(self);
				};
				<>c.<>9__40_0 = val;
				obj = (object)val;
			}
			BossGroup.DropRewards += (hook_DropRewards)obj;
			SceneManager.activeSceneChanged += delegate(Scene oldScene, Scene newScene)
			{
				bool flag = ((Scene)(ref newScene)).name == "loadingbasic" || ((Scene)(ref newScene)).name == "intro" || ((Scene)(ref newScene)).name == "splash" || ((Scene)(ref newScene)).name == "title" || ((Scene)(ref newScene)).name == "eclipseworld" || ((Scene)(ref newScene)).name == "infinitetowerworld" || ((Scene)(ref newScene)).name == "lobby";
				forceDisabled = ((Scene)(ref newScene)).name == "artifactworld" || flag;
				if (flag)
				{
					useManualDropRate = false;
					DropRateTracker.ResetTracker();
				}
				ReevaluateLifecycle();
			};
			NetworkingAPI.RegisterMessageType<SpawnCustomMessage2>();
		}

		private void ReevaluateLifecycle()
		{
			if (EnablePlayerDropRateScaling.Value && IsSacrificeEnabled() && !useManualDropRate)
			{
				dropChance = dropRateManager.GetPlayerAwareBaseDropChance(GetPlayerCount(), dropChance);
			}
			if (forceDisabled)
			{
				if (IsModHooked)
				{
					Log.Warning("Force Disabled: Will remove hooks, even if Artifact of Command is enabled.");
					UnHook();
				}
				else
				{
					Log.Debug("Force Disabled: Will not hook.");
				}
				return;
			}
			bool flag = IsCommandEnabled();
			if (IsModHooked && !flag)
			{
				UnHook();
			}
			else if (!IsModHooked && flag)
			{
				Hook();
			}
		}

		private void Hook()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Loading hooks");
			object obj = <>O.<0>__Util_GetExpAdjustedDropChancePercent;
			if (obj == null)
			{
				hook_GetExpAdjustedDropChancePercent val = Util_GetExpAdjustedDropChancePercent;
				<>O.<0>__Util_GetExpAdjustedDropChancePercent = val;
				obj = (object)val;
			}
			Util.GetExpAdjustedDropChancePercent += (hook_GetExpAdjustedDropChancePercent)obj;
			NetworkMessageHandlerAttribute.RegisterClientMessages += new hook_RegisterClientMessages(NetworkMessageHandlerAttribute_RegisterClientMessages);
			PickupDropletController.CreateCommandCube += new hook_CreateCommandCube(PickupDropletController_CreateCommandCube);
			Interactor.AttemptInteraction += new hook_AttemptInteraction(Interactor_AttemptInteraction);
			PickupPickerController.SubmitChoice += new hook_SubmitChoice(PickupPickerController_SubmitChoice);
			GenericPickupController.CreatePickup += new hook_CreatePickup(GenericPickupController_CreatePickup);
			IsModHooked = true;
		}

		private void UnHook()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Unloading hooks");
			object obj = <>O.<0>__Util_GetExpAdjustedDropChancePercent;
			if (obj == null)
			{
				hook_GetExpAdjustedDropChancePercent val = Util_GetExpAdjustedDropChancePercent;
				<>O.<0>__Util_GetExpAdjustedDropChancePercent = val;
				obj = (object)val;
			}
			Util.GetExpAdjustedDropChancePercent -= (hook_GetExpAdjustedDropChancePercent)obj;
			NetworkMessageHandlerAttribute.RegisterClientMessages -= new hook_RegisterClientMessages(NetworkMessageHandlerAttribute_RegisterClientMessages);
			PickupDropletController.CreateCommandCube -= new hook_CreateCommandCube(PickupDropletController_CreateCommandCube);
			Interactor.AttemptInteraction -= new hook_AttemptInteraction(Interactor_AttemptInteraction);
			PickupPickerController.SubmitChoice -= new hook_SubmitChoice(PickupPickerController_SubmitChoice);
			GenericPickupController.CreatePickup -= new hook_CreatePickup(GenericPickupController_CreatePickup);
			IsModHooked = false;
		}

		private GenericPickupController GenericPickupController_CreatePickup(orig_CreatePickup orig, ref CreatePickupInfo createPickupInfo)
		{
			return null;
		}

		private void PickupPickerController_SubmitChoice(orig_SubmitChoice orig, PickupPickerController self, int choiceIndex)
		{
			//IL_0031: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if ((ulong)choiceIndex >= (ulong)self.options.Length)
			{
				return;
			}
			ref Option reference = ref self.options[choiceIndex];
			if (reference.available)
			{
				PickupIndexUnityEvent onPickupSelected = self.onPickupSelected;
				if (onPickupSelected != null)
				{
					((UnityEvent<int>)(object)onPickupSelected).Invoke(((Option)(ref reference)).pickupIndex.value);
					Log.Info($"Sending CreatePickup {((Option)(ref self.options[choiceIndex])).pickupIndex}");
					NetMessageExtensions.Send((INetMessage)(object)new SpawnCustomMessage2(((Option)(ref self.options[choiceIndex])).pickupIndex, ((MPEventSystem)EventSystem.current).localUser.cachedMaster.GetBodyObject().transform.position), (NetworkDestination)2);
					self.OnDisplayEnd((NetworkUIPromptController)null, (LocalUser)null, (CameraRigController)null);
					Object.Destroy((Object)(object)((Component)self).gameObject);
				}
			}
		}

		private void Interactor_AttemptInteraction(orig_AttemptInteraction orig, Interactor self, GameObject interactableObject)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (((Object)interactableObject).name.StartsWith("CommandCube"))
			{
				interactableObject.GetComponent<PickupPickerController>().OnDisplayBegin((NetworkUIPromptController)null, (LocalUser)null, ((MPEventSystem)EventSystem.current).localUser.cameraRigController);
			}
			else
			{
				orig.Invoke(self, interactableObject);
			}
		}

		private void PickupDropletController_CreateCommandCube(orig_CreateCommandCube orig, PickupDropletController self)
		{
			//IL_0001: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: 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_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			DropRateTracker.RegisterItemDrop(self.createPickupInfo);
			NetworkServer.SendToAll((short)969, (MessageBase)(object)new SpawnCustomMessage
			{
				position = self.createPickupInfo.position,
				pickupIndex = ((CreatePickupInfo)(ref self.createPickupInfo)).pickupIndex
			});
			if (!NetworkServer.active)
			{
				return;
			}
			ReadOnlyCollection<NetworkUser> readOnlyInstancesList = NetworkUser.readOnlyInstancesList;
			if (readOnlyInstancesList == null)
			{
				return;
			}
			Vector3 val = default(Vector3);
			foreach (NetworkUser item in readOnlyInstancesList)
			{
				if ((Object)(object)item == (Object)null || ((NetworkBehaviour)item).connectionToClient == null || !((NetworkBehaviour)item).connectionToClient.isReady || (Object)(object)item.master == (Object)null || !item.master.hasBody)
				{
					continue;
				}
				ItemTier? catchupTier = PlayerCatchupManager.GetCatchupTier(item);
				if (!catchupTier.HasValue)
				{
					continue;
				}
				int deficit = PlayerCatchupManager.GetDeficit(item, catchupTier.Value);
				float catchupChance = PlayerCatchupManager.GetCatchupChance(deficit, catchupTier.Value);
				if (Random.value < catchupChance)
				{
					PickupIndex randomPickupForTier = PlayerCatchupManager.GetRandomPickupForTier(catchupTier.Value);
					if (((PickupIndex)(ref randomPickupForTier)).isValid)
					{
						((Vector3)(ref val))..ctor(Random.Range(-1.5f, 1.5f), 0f, Random.Range(-1.5f, 1.5f));
						SpawnCustomMessage spawnCustomMessage = new SpawnCustomMessage
						{
							position = self.createPickupInfo.position + val,
							pickupIndex = randomPickupForTier
						};
						((NetworkBehaviour)item).connectionToClient.Send((short)969, (MessageBase)(object)spawnCustomMessage);
						Log.Info($"Catchup drop for {item.userName}: tier={catchupTier.Value} deficit={deficit} chance={catchupChance:P} pickup={randomPickupForTier.value}");
					}
				}
			}
		}

		private void NetworkMessageHandlerAttribute_RegisterClientMessages(orig_RegisterClientMessages orig, NetworkClient client)
		{
			//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_0028: Expected O, but got Unknown
			orig.Invoke(client);
			object obj = <>O.<1>__HandlePickupMessage;
			if (obj == null)
			{
				NetworkMessageDelegate val = SpawnCustomController.HandlePickupMessage;
				<>O.<1>__HandlePickupMessage = val;
				obj = (object)val;
			}
			client.RegisterHandler((short)969, (NetworkMessageDelegate)obj);
		}

		private static float Util_GetExpAdjustedDropChancePercent(orig_GetExpAdjustedDropChancePercent orig, float nativeBaseDropChance, GameObject characterBodyObject)
		{
			float num = orig.Invoke(nativeBaseDropChance, characterBodyObject);
			float num2 = dropRateManager.ComputeDropChance(IsSwarmEnabled(), num, nativeBaseDropChance, dropChance);
			Log.Debug($"DropChance: original_base={nativeBaseDropChance} original_final={num}; plugin_base={dropChance}; plugin_final={num2} (Character={((Object)characterBodyObject).name})");
			DropRateTracker.RegisterDropOpportunity(num2);
			if (dropRateManager.NeedsBadLuckProtection(num2, Run.instance.GetRunStopwatch()))
			{
				return dropRateManager.GetBadLuckProtectedDropChance(num2);
			}
			return num2;
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void CCSetDropRate(ConCommandArgs args)
		{
			dropChance = ((ConCommandArgs)(ref args)).GetArgFloat(0);
			useManualDropRate = true;
			Log.Info("Drop rate set to: " + ((ConCommandArgs)(ref args)).GetArgString(0) + "%");
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void CCGetDropRateReport(ConCommandArgs args)
		{
			if (IsModHooked)
			{
				DropRateTracker.LogReport();
			}
		}

		private static bool IsSacrificeEnabled()
		{
			if (RunArtifactManager.instance != null && ((Behaviour)RunArtifactManager.instance).isActiveAndEnabled)
			{
				return RunArtifactManager.instance.IsArtifactEnabled(Artifacts.sacrificeArtifactDef);
			}
			return false;
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void CCSetHotkey(ConCommandArgs args)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (IsModHooked)
			{
				string argString = ((ConCommandArgs)(ref args)).GetArgString(0);
				if (Enum.TryParse<KeyCode>(argString, ignoreCase: true, out KeyCode result))
				{
					PullItemsHotKey.Value = result;
				}
				else
				{
					Log.Warning("Failed to set hotkey to " + argString);
				}
			}
		}

		private static bool IsCommandEnabled()
		{
			if (RunArtifactManager.instance != null && ((Behaviour)RunArtifactManager.instance).isActiveAndEnabled)
			{
				return RunArtifactManager.instance.IsArtifactEnabled(Artifacts.commandArtifactDef);
			}
			return false;
		}

		private static bool IsSwarmEnabled()
		{
			if (RunArtifactManager.instance != null && ((Behaviour)RunArtifactManager.instance).isActiveAndEnabled)
			{
				return RunArtifactManager.instance.IsArtifactEnabled(Artifacts.swarmsArtifactDef);
			}
			return false;
		}

		private int GetPlayerCount()
		{
			return PlayerCharacterMasterController.instances.Count((PlayerCharacterMasterController pc) => pc.isConnected);
		}
	}
	public class DropRateManager
	{
		private ConfigEntry<bool> EnableBadLuckProtection;

		private ConfigEntry<bool> EnableSwarmsScaling;

		private ConfigEntry<float> DropChanceMultiplier;

		private ConfigEntry<float> BaseDropChance;

		private ConfigEntry<float> MinimumDropChance;

		public DropRateManager(ConfigEntry<bool> enableBadLuckProtection, ConfigEntry<bool> enableSwarmsScaling, ConfigEntry<float> dropChanceMultiplier, ConfigEntry<float> baseDropChance, ConfigEntry<float> minimumDropChance)
		{
			EnableBadLuckProtection = enableBadLuckProtection;
			EnableSwarmsScaling = enableSwarmsScaling;
			DropChanceMultiplier = dropChanceMultiplier;
			BaseDropChance = baseDropChance;
			MinimumDropChance = minimumDropChance;
		}

		public float GetPlayerAwareBaseDropChance(int playerCount, float dropChance)
		{
			float num = 1f / (float)playerCount * DropChanceMultiplier.Value;
			float num2 = Math.Max(BaseDropChance.Value * num, MinimumDropChance.Value);
			if (!dropChance.Equals(num2))
			{
				dropChance = num2;
				Log.Debug($"Using dropRate of {dropChance}% (base={BaseDropChance.Value}, multiplier={DropChanceMultiplier.Value}, playerCount={playerCount}, minimumDropChance={MinimumDropChance.Value}");
			}
			return dropChance;
		}

		public float ComputeDropChance(bool isSwarmEnabled, float nativeDropChance, float nativeBaseDropChance, float dropChance)
		{
			float num = ((!(EnableSwarmsScaling.Value && isSwarmEnabled) || nativeDropChance == 0f) ? nativeDropChance : ((nativeDropChance - nativeBaseDropChance) * 0.6f + nativeBaseDropChance));
			float num2 = nativeBaseDropChance / 5f;
			float num3 = num / nativeBaseDropChance;
			return dropChance * num2 * num3;
		}

		public bool NeedsBadLuckProtection(float actualDropChance, float time)
		{
			if (EnableBadLuckProtection.Value && DropRateTracker.cumulativeDropChance >= 100f && actualDropChance > 0f)
			{
				bool num = DropRateTracker.expectedItems > (float)DropRateTracker.totalItemDrops;
				float num2 = (float)DropRateTracker.totalItemDrops / (time / 60f);
				if (!num)
				{
					return (double)num2 < 0.8;
				}
				return true;
			}
			return false;
		}

		public float GetBadLuckProtectedDropChance(float dropChance)
		{
			float num = DropRateTracker.cumulativeDropChance - 100f;
			float num2 = Math.Min(dropChance + num, 90f);
			Log.Debug($"Bad Luck Protection triggered! ({dropChance}% => {num2}%)");
			return num2;
		}
	}
	public static class DropRateTracker
	{
		private static Dictionary<float, long> DropRateReport = new Dictionary<float, long>();

		public static long totalItemDrops { get; private set; } = 0L;


		public static float cumulativeDropChance { get; private set; } = 0f;


		public static float expectedItems { get; private set; } = 0f;


		public static void ResetTracker()
		{
			DropRateReport = new Dictionary<float, long>();
			totalItemDrops = 0L;
			cumulativeDropChance = 0f;
			expectedItems = 0f;
		}

		public static void RegisterDropOpportunity(float dropChance)
		{
			cumulativeDropChance += dropChance;
			expectedItems += (float)Math.Round(dropChance / 100f, 2);
			if (DropRateReport.TryGetValue(dropChance, out var _))
			{
				DropRateReport[dropChance]++;
			}
			else
			{
				DropRateReport.Add(dropChance, 1L);
			}
		}

		public static void RegisterItemDrop(CreatePickupInfo createPickupInfo)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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)
			PickupIndex pickupIndex = ((CreatePickupInfo)(ref createPickupInfo)).pickupIndex;
			if (((PickupIndex)(ref pickupIndex)).pickupDef != null)
			{
				pickupIndex = ((CreatePickupInfo)(ref createPickupInfo)).pickupIndex;
				PickupDef pickupDef = ((PickupIndex)(ref pickupIndex)).pickupDef;
				if (pickupDef.coinValue == 0 && pickupDef.nameToken != "PICKUP_LUNAR_COIN")
				{
					cumulativeDropChance = 0f;
					totalItemDrops++;
				}
			}
		}

		public static void LogReport()
		{
			if (Run.instance == null || !((Behaviour)Run.instance).isActiveAndEnabled)
			{
				return;
			}
			long num = DropRateReport.Values.Aggregate(0L, (long i, long l) => i + l);
			if (num != 0L)
			{
				string text = DropRateReport.Keys.OrderByDescending((float f) => f).Aggregate("Kills by Drop Rate", (string s, float f) => $"{s}\n  {f}%: {DropRateReport[f]} kills");
				float runStopwatch = Run.instance.GetRunStopwatch();
				float num2 = runStopwatch / 60f;
				float num3 = DropRateReport.Keys.Aggregate(0f, (float f, float f1) => f + (float)DropRateReport[f1] * f1) / (float)num;
				Log.Info(string.Concat(string.Concat(string.Concat(string.Concat(string.Concat(string.Concat(string.Concat(string.Concat("------ DROP RATE RUN REPORT -----\n" + $"Stage: {Run.instance.stageClearCount + 1}\n", $"Run Duration: {runStopwatch}s\n"), $"Total kills: {num}\n"), $"Total items: {totalItemDrops}\n"), $"Theoretical Average Drop Rate: {num3}%\n"), $"Actual Average Drop Rate: {(float)totalItemDrops / (float)num * 100f}%\n"), $"Drops per Minute: {Math.Round((float)totalItemDrops / num2, 2)}\n"), text, "\n"), "------ END OF REPORT -----"));
			}
		}
	}
	internal static class Log
	{
		private static ManualLogSource _logSource;

		internal static void Init(ManualLogSource logSource)
		{
			_logSource = logSource;
		}

		internal static void Debug(object data)
		{
			_logSource.LogDebug(data);
		}

		internal static void Error(object data)
		{
			_logSource.LogError(data);
		}

		internal static void Fatal(object data)
		{
			_logSource.LogFatal(data);
		}

		internal static void Info(object data)
		{
			_logSource.LogInfo(data);
		}

		internal static void Message(object data)
		{
			_logSource.LogMessage(data);
		}

		internal static void Warning(object data)
		{
			_logSource.LogWarning(data);
		}
	}
	public static class PlayerCatchupManager
	{
		private static readonly Dictionary<ItemTier, float> TierWeights = new Dictionary<ItemTier, float>
		{
			[(ItemTier)0] = 1f,
			[(ItemTier)1] = 3f,
			[(ItemTier)2] = 10f,
			[(ItemTier)4] = 15f
		};

		private static readonly HashSet<ItemTier> IgnoredTiers = new HashSet<ItemTier>
		{
			(ItemTier)3,
			(ItemTier)6,
			(ItemTier)7,
			(ItemTier)8,
			(ItemTier)9,
			(ItemTier)5
		};

		public static ItemTier? GetCatchupTier(NetworkUser user)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			List<NetworkUser> players = GetPlayers();
			if (players.Count <= 1)
			{
				return null;
			}
			Dictionary<uint, Dictionary<ItemTier, int>> dictionary = new Dictionary<uint, Dictionary<ItemTier, int>>();
			NetworkInstanceId netId;
			foreach (NetworkUser item in players)
			{
				object obj;
				if (item == null)
				{
					obj = null;
				}
				else
				{
					CharacterMaster master = item.master;
					obj = ((master != null) ? master.inventory : null);
				}
				if ((Object)obj != (Object)null)
				{
					netId = ((NetworkBehaviour)item).netId;
					dictionary[((NetworkInstanceId)(ref netId)).Value] = CountItemsByTier(item);
				}
			}
			if (dictionary.Count <= 1)
			{
				return null;
			}
			netId = ((NetworkBehaviour)user).netId;
			if (!dictionary.TryGetValue(((NetworkInstanceId)(ref netId)).Value, out var value))
			{
				return null;
			}
			Dictionary<ItemTier, int> dictionary2 = new Dictionary<ItemTier, int>();
			foreach (ItemTier tier in TierWeights.Keys)
			{
				dictionary2[tier] = dictionary.Values.Select((Dictionary<ItemTier, int> c) => c.TryGetValue(tier, out var value3) ? value3 : 0).Max();
			}
			ItemTier? result = null;
			float num = 0f;
			foreach (ItemTier key in TierWeights.Keys)
			{
				int value2;
				int num2 = dictionary2[key] - (value.TryGetValue(key, out value2) ? value2 : 0);
				if (num2 > 0)
				{
					float num3 = (float)num2 * TierWeights[key];
					if (num3 > num)
					{
						num = num3;
						result = key;
					}
				}
			}
			return result;
		}

		public static int GetDeficit(NetworkUser user, ItemTier tier)
		{
			//IL_0007: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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_005c: 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)
			List<NetworkUser> players = GetPlayers();
			if (players.Count <= 1)
			{
				return 0;
			}
			Dictionary<uint, Dictionary<ItemTier, int>> dictionary = new Dictionary<uint, Dictionary<ItemTier, int>>();
			NetworkInstanceId netId;
			foreach (NetworkUser item in players)
			{
				object obj;
				if (item == null)
				{
					obj = null;
				}
				else
				{
					CharacterMaster master = item.master;
					obj = ((master != null) ? master.inventory : null);
				}
				if ((Object)obj != (Object)null)
				{
					netId = ((NetworkBehaviour)item).netId;
					dictionary[((NetworkInstanceId)(ref netId)).Value] = CountItemsByTier(item);
				}
			}
			netId = ((NetworkBehaviour)user).netId;
			if (!dictionary.TryGetValue(((NetworkInstanceId)(ref netId)).Value, out var value))
			{
				return 0;
			}
			int value2;
			int num = (value.TryGetValue(tier, out value2) ? value2 : 0);
			int value3;
			int num2 = dictionary.Values.Select((Dictionary<ItemTier, int> dict) => dict.TryGetValue(tier, out value3) ? value3 : 0).Max();
			return Math.Max(0, num2 - num);
		}

		public static float GetCatchupChance(int deficit, ItemTier tier)
		{
			if (deficit <= 0)
			{
				return 0f;
			}
			return Math.Min((float)deficit * 0.05f, 0.5f);
		}

		public static PickupIndex GetRandomPickupForTier(ItemTier tier)
		{
			//IL_000d: 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_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected I4, but got Unknown
			//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_0094: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Run.instance == (Object)null)
			{
				return PickupIndex.none;
			}
			try
			{
				List<PickupIndex> list = (int)tier switch
				{
					0 => Run.instance.availableTier1DropList, 
					1 => Run.instance.availableTier2DropList, 
					2 => Run.instance.availableTier3DropList, 
					4 => Run.instance.availableBossDropList, 
					_ => null, 
				};
				if (list != null && list.Count > 0)
				{
					return list[Random.Range(0, list.Count)];
				}
			}
			catch
			{
			}
			return PickupIndex.none;
		}

		private static List<NetworkUser> GetPlayers()
		{
			if (NetworkUser.readOnlyInstancesList != null && NetworkUser.readOnlyInstancesList.Count > 0)
			{
				return NetworkUser.readOnlyInstancesList.ToList();
			}
			return (from p in PlayerCharacterMasterController.instances
				where (Object)(object)((p != null) ? p.networkUser : null) != (Object)null
				select p.networkUser).ToList();
		}

		private static Dictionary<ItemTier, int> CountItemsByTier(NetworkUser user)
		{
			//IL_006d: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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)
			Dictionary<ItemTier, int> dictionary = new Dictionary<ItemTier, int>();
			object obj;
			if (user == null)
			{
				obj = null;
			}
			else
			{
				CharacterMaster master = user.master;
				obj = ((master != null) ? master.inventory : null);
			}
			Inventory val = (Inventory)obj;
			if ((Object)(object)val == (Object)null)
			{
				return dictionary;
			}
			foreach (ItemTier value in Enum.GetValues(typeof(ItemTier)))
			{
				if (!IgnoredTiers.Contains(value))
				{
					try
					{
						dictionary[value] = val.GetTotalItemCountOfTier(value);
					}
					catch
					{
						dictionary[value] = 0;
					}
				}
			}
			return dictionary;
		}
	}
	public class SpawnCustomController : NetworkBehaviour
	{
		[NetworkMessageHandler(msgType = 969, client = true)]
		public static void HandlePickupMessage(NetworkMessage netMsg)
		{
			//IL_002d: 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)
			SpawnCustomMessage spawnCustomMessage = new SpawnCustomMessage();
			netMsg.ReadMessage<SpawnCustomMessage>(spawnCustomMessage);
			Log.Info($"HandlePickupMessage {spawnCustomMessage.pickupIndex.value}");
			CreatePickupDroplet(spawnCustomMessage.pickupIndex, spawnCustomMessage.position);
		}

		public static void CreatePickupDroplet(PickupIndex pickupIndex, Vector3 position)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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)
			//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_001e: 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_002c: 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_002e: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			CreatePickupInfo val = default(CreatePickupInfo);
			val.rotation = Quaternion.identity;
			((CreatePickupInfo)(ref val)).pickupIndex = pickupIndex;
			CreatePickupInfo val2 = val;
			UniquePickup val3 = default(UniquePickup);
			((UniquePickup)(ref val3))..ctor(pickupIndex);
			GameObject obj = Object.Instantiate<GameObject>(CommandArtifactManager.commandCubePrefab, position, val2.rotation);
			PickupIndexNetworker component = obj.GetComponent<PickupIndexNetworker>();
			PickupPickerController component2 = obj.GetComponent<PickupPickerController>();
			component2.SetOptionsInternal(PickupPickerController.GetOptionsFromPickupState(val3));
			if (!NetworkServer.active)
			{
				((NetworkBehaviour)component2).SetDirtyBit(PickupPickerController.optionsDirtyBit);
			}
			component.NetworkpickupState = val3;
			if (Object.op_Implicit((Object)(object)component.pickupDisplay))
			{
				component.pickupDisplay.SetPickup(ref val3, false);
			}
		}

		public static void CreatePickupItem(PickupIndex pickupIndex, Vector3 position)
		{
			//IL_000a: 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_0017: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_002e: 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_0037: 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_0048: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			if (NetworkServer.active)
			{
				CreatePickupInfo val = default(CreatePickupInfo);
				val.rotation = Quaternion.identity;
				val.position = position;
				((CreatePickupInfo)(ref val)).pickupIndex = pickupIndex;
				CreatePickupInfo val2 = val;
				UniquePickup val3 = default(UniquePickup);
				((UniquePickup)(ref val3))..ctor(pickupIndex);
				GameObject obj = Object.Instantiate<GameObject>(val2.prefabOverride ?? GenericPickupController.pickupPrefab, position, val2.rotation);
				GenericPickupController component = obj.GetComponent<GenericPickupController>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.Network_pickupState = val3;
				}
				PickupIndexNetworker component2 = obj.GetComponent<PickupIndexNetworker>();
				if (Object.op_Implicit((Object)(object)component2))
				{
					component2.NetworkpickupState = val3;
				}
				PickupPickerController component3 = obj.GetComponent<PickupPickerController>();
				if (Object.op_Implicit((Object)(object)component3) && val2.pickerOptions != null)
				{
					component3.SetOptionsServer(val2.pickerOptions);
				}
				NetworkServer.Spawn(obj);
			}
		}
	}
	public class SpawnCustomMessage : MessageBase, INetMessage, ISerializableObject
	{
		public Vector3 position;

		public PickupIndex pickupIndex;

		public override void Serialize(NetworkWriter writer)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			writer.Write(position);
			writer.Write(pickupIndex.value);
		}

		public override void Deserialize(NetworkReader reader)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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)
			position = reader.ReadVector3();
			pickupIndex = new PickupIndex(reader.ReadInt32());
		}

		public void OnReceived()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			SpawnCustomController.CreatePickupItem(pickupIndex, position);
		}
	}
	public struct SpawnCustomMessage2 : INetMessage, ISerializableObject
	{
		public Vector3 position;

		public PickupIndex pickupIndex;

		public void Serialize(NetworkWriter writer)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			writer.Write(position);
			writer.Write(pickupIndex.value);
		}

		public void Deserialize(NetworkReader reader)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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)
			position = reader.ReadVector3();
			pickupIndex = new PickupIndex(reader.ReadInt32());
		}

		public void OnReceived()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			SpawnCustomController.CreatePickupItem(pickupIndex, position);
		}

		public SpawnCustomMessage2(PickupIndex pickupIndex, Vector3 position)
		{
			//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_0009: Unknown result type (might be due to invalid IL or missing references)
			this.position = position;
			this.pickupIndex = pickupIndex;
		}
	}
}