Decompiled source of ExtendedSacrifice v1.0.0

ExtendedSacrifice.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExtendedSacrifice.Compatibility;
using ExtendedSacrifice.Configuration;
using ExtendedSacrifice.Drops;
using Microsoft.CodeAnalysis;
using On.RoR2;
using On.RoR2.Artifacts;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.Artifacts;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ExtendedSacrifice")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+b28b33f5b9a60c2a7fd3c5364630b813641d2951")]
[assembly: AssemblyProduct("ExtendedSacrifice")]
[assembly: AssemblyTitle("ExtendedSacrifice")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ExtendedSacrifice
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("Irodflash.ExtendedSacrifice", "ExtendedSacrifice", "1.0.0")]
	public class ExtendedSacrificePlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "Irodflash.ExtendedSacrifice";

		public const string PluginAuthor = "Irodflash";

		public const string PluginName = "ExtendedSacrifice";

		public const string PluginVersion = "1.0.0";

		private SacrificeDropHandler _dropHandler;

		public void Awake()
		{
			Log.Init(((BaseUnityPlugin)this).Logger);
			PluginConfig config = new PluginConfig(((BaseUnityPlugin)this).Config);
			if (Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions"))
			{
				RiskOfOptionsCompat.Register(config);
			}
			_dropHandler = new SacrificeDropHandler(config, new PityTracker());
			_dropHandler.Hook();
			Log.Info("Awake done.");
		}

		public void OnDestroy()
		{
			_dropHandler?.Unhook();
		}
	}
	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);
		}
	}
}
namespace ExtendedSacrifice.Drops
{
	internal static class DropChanceCalculator
	{
		public static float Compute(PluginConfig config, CharacterBody victimBody, GameObject victimObject)
		{
			float num = config.BaseDropChance.Value * config.DropChanceMultiplier.Value;
			if (Object.op_Implicit((Object)(object)victimBody))
			{
				if (victimBody.isElite)
				{
					num *= config.EliteMultiplier.Value;
				}
				if (victimBody.isBoss || victimBody.isChampion)
				{
					num *= config.BossMultiplier.Value;
				}
			}
			num *= 1f + GetStageBonus(config);
			num = Util.GetExpAdjustedDropChancePercent(num, victimObject);
			return Mathf.Clamp(num, 0f, config.MaxDropChance.Value);
		}

		private static float GetStageBonus(PluginConfig config)
		{
			if (!config.StageScalingEnabled.Value || !Object.op_Implicit((Object)(object)Run.instance))
			{
				return 0f;
			}
			return Mathf.Min((float)Run.instance.stageClearCount * config.BonusPerStage.Value, config.MaxStageBonus.Value) / 100f;
		}
	}
	internal sealed class PityTracker
	{
		public int KillsWithoutDrop { get; private set; }

		public void Hook()
		{
			Run.onRunStartGlobal += OnRunStart;
		}

		public void Unhook()
		{
			Run.onRunStartGlobal -= OnRunStart;
		}

		public bool IsGuaranteed(int threshold)
		{
			return KillsWithoutDrop >= threshold;
		}

		public void RegisterMiss()
		{
			KillsWithoutDrop++;
		}

		public void RegisterDrop()
		{
			KillsWithoutDrop = 0;
		}

		private void OnRunStart(Run run)
		{
			KillsWithoutDrop = 0;
		}
	}
	internal sealed class SacrificeDropHandler
	{
		private readonly PluginConfig _config;

		private readonly PityTracker _pity;

		private readonly SacrificeDropRoller _roller;

		private readonly HashSet<HealthComponent> _rolledVictims = new HashSet<HealthComponent>();

		private bool _warnedInterruptedDeath;

		public SacrificeDropHandler(PluginConfig config, PityTracker pity)
		{
			_config = config;
			_pity = pity;
			_roller = new SacrificeDropRoller(config, pity);
		}

		public void Hook()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			SacrificeArtifactManager.OnServerCharacterDeath += new hook_OnServerCharacterDeath(SkipVanillaRoll);
			HealthComponent.TakeDamageProcess += new hook_TakeDamageProcess(GuardTakeDamageProcess);
			GlobalEventManager.onCharacterDeathServer += OnCharacterDeathServer;
			Stage.onServerStageBegin += OnServerStageBegin;
			Stage.onStageStartGlobal += LogStageStatus;
			_pity.Hook();
		}

		public void Unhook()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			SacrificeArtifactManager.OnServerCharacterDeath -= new hook_OnServerCharacterDeath(SkipVanillaRoll);
			HealthComponent.TakeDamageProcess -= new hook_TakeDamageProcess(GuardTakeDamageProcess);
			GlobalEventManager.onCharacterDeathServer -= OnCharacterDeathServer;
			Stage.onServerStageBegin -= OnServerStageBegin;
			Stage.onStageStartGlobal -= LogStageStatus;
			_pity.Unhook();
		}

		private void SkipVanillaRoll(orig_OnServerCharacterDeath orig, DamageReport damageReport)
		{
			if (!_config.Enabled.Value)
			{
				orig.Invoke(damageReport);
			}
		}

		private void OnCharacterDeathServer(DamageReport damageReport)
		{
			if (ShouldRoll() && Object.op_Implicit((Object)(object)damageReport.victim))
			{
				_rolledVictims.Add(damageReport.victim);
				SafeRoll(damageReport);
			}
		}

		private void GuardTakeDamageProcess(orig_TakeDamageProcess orig, HealthComponent self, DamageInfo damageInfo)
		{
			bool alive = self.alive;
			float combinedHealth = self.combinedHealth;
			try
			{
				orig.Invoke(self, damageInfo);
			}
			finally
			{
				if (alive && !self.alive)
				{
					OnDiedDuringHit(self, damageInfo, combinedHealth);
				}
			}
		}

		private void OnDiedDuringHit(HealthComponent victim, DamageInfo damageInfo, float combinedHealthBefore)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			if (!_rolledVictims.Remove(victim) && ShouldRoll())
			{
				if (!_warnedInterruptedDeath)
				{
					_warnedInterruptedDeath = true;
					Log.Warning("Death of " + ((Object)victim).name + " was interrupted by another mod's error (see the stack trace above); Sacrifice drops are rolled anyway. Further occurrences are only logged with Verbose Logging.");
				}
				else if (_config.VerboseLogging.Value)
				{
					Log.Info("Death of " + ((Object)victim).name + " interrupted by another mod, rolling anyway.");
				}
				SafeRoll(new DamageReport(damageInfo, victim, damageInfo.damage, combinedHealthBefore));
			}
		}

		private void OnServerStageBegin(Stage stage)
		{
			_rolledVictims.Clear();
		}

		private void LogStageStatus(Stage stage)
		{
			SceneDef sceneDef = stage.sceneDef;
			Log.Info($"Stage {((sceneDef != null) ? sceneDef.cachedName : null)}: mod enabled: {_config.Enabled.Value}, host: {NetworkServer.active}, " + $"Sacrifice active: {IsSacrificeActive()} -> drops handled here: {ShouldRoll()}");
		}

		private bool ShouldRoll()
		{
			if (_config.Enabled.Value && NetworkServer.active)
			{
				return IsSacrificeActive();
			}
			return false;
		}

		private static bool IsSacrificeActive()
		{
			if (Object.op_Implicit((Object)(object)RunArtifactManager.instance))
			{
				return RunArtifactManager.instance.IsArtifactEnabled(Artifacts.sacrificeArtifactDef);
			}
			return false;
		}

		private void SafeRoll(DamageReport damageReport)
		{
			try
			{
				_roller.Roll(damageReport);
			}
			catch (Exception arg)
			{
				Log.Error($"Sacrifice drop roll failed: {arg}");
			}
		}
	}
	internal sealed class SacrificeDropRoller
	{
		private const float DropUpwardSpeed = 20f;

		private const float JackpotUpwardSpeed = 30f;

		private const float JackpotSpreadSpeed = 6f;

		private readonly PluginConfig _config;

		private readonly PityTracker _pity;

		private bool _warnedMissingDropTable;

		public SacrificeDropRoller(PluginConfig config, PityTracker pity)
		{
			_config = config;
			_pity = pity;
		}

		public void Roll(DamageReport damageReport)
		{
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			if (!IsEligible(damageReport))
			{
				return;
			}
			CharacterBody victimBody = damageReport.victimBody;
			float num = DropChanceCalculator.Compute(_config, victimBody, ((Component)damageReport.victim).gameObject);
			bool flag = _config.PityEnabled.Value && _pity.IsGuaranteed(_config.KillsBeforeGuaranteedDrop.Value);
			bool flag2 = flag || Util.CheckRoll(num, 0f, (CharacterMaster)null);
			bool flag3 = flag2 && _config.JackpotEnabled.Value && Util.CheckRoll(_config.JackpotChance.Value, 0f, (CharacterMaster)null);
			if (_config.VerboseLogging.Value)
			{
				DeathRewards component = ((Component)victimBody).GetComponent<DeathRewards>();
				int num2 = (Object.op_Implicit((Object)(object)component) ? component.spawnValue : 0);
				Log.Info($"Kill {victimBody.GetDisplayName()} (elite: {victimBody.isElite}, boss: {victimBody.isBoss || victimBody.isChampion}, " + $"spawnValue: {num2}) chance: {num:0.##}%, misses: {_pity.KillsWithoutDrop}, " + $"dropped: {flag2}, guaranteed: {flag}, jackpot: {flag3}");
			}
			if (!flag2)
			{
				_pity.RegisterMiss();
				return;
			}
			_pity.RegisterDrop();
			Vector3 corePosition = victimBody.corePosition;
			if (flag3)
			{
				DropJackpot(corePosition, _config.JackpotItemCount.Value);
			}
			else
			{
				TryDrop(corePosition, Vector3.up * 20f);
			}
		}

		private static bool IsEligible(DamageReport damageReport)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)damageReport.victimMaster) || !Object.op_Implicit((Object)(object)damageReport.victimBody) || !Object.op_Implicit((Object)(object)damageReport.victim))
			{
				return false;
			}
			if (damageReport.attackerTeamIndex == damageReport.victimTeamIndex)
			{
				return !Object.op_Implicit((Object)(object)damageReport.victimMaster.minionOwnership.ownerMaster);
			}
			return true;
		}

		private void DropJackpot(Vector3 position, int count)
		{
			//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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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_0061: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			Quaternion val = Quaternion.AngleAxis(360f / (float)count, Vector3.up);
			Vector3 val2 = Quaternion.AngleAxis(Random.Range(0f, 360f), Vector3.up) * (Vector3.up * 30f + Vector3.forward * 6f);
			for (int i = 0; i < count; i++)
			{
				TryDrop(position, val2);
				val2 = val * val2;
			}
		}

		private void TryDrop(Vector3 position, Vector3 velocity)
		{
			//IL_002e: 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_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_003f: Unknown result type (might be due to invalid IL or missing references)
			PickupDropTable dropTable = SacrificeArtifactManager.dropTable;
			if (!Object.op_Implicit((Object)(object)dropTable))
			{
				if (!_warnedMissingDropTable)
				{
					_warnedMissingDropTable = true;
					Log.Warning("Sacrifice drop table is not loaded, no item can drop.");
				}
				return;
			}
			UniquePickup val = dropTable.GeneratePickup(SacrificeArtifactManager.treasureRng);
			if (((UniquePickup)(ref val)).isValid)
			{
				PickupDropletController.CreatePickupDroplet(val, position, velocity, false);
			}
			else if (_config.VerboseLogging.Value)
			{
				Log.Warning("Sacrifice drop table returned no valid pickup.");
			}
		}
	}
}
namespace ExtendedSacrifice.Configuration
{
	internal sealed class PluginConfig
	{
		private const string GeneralSection = "1. General";

		private const string EliteBossSection = "2. Elites and Bosses";

		private const string StageScalingSection = "3. Stage Scaling";

		private const string PitySection = "4. Pity";

		private const string JackpotSection = "5. Jackpot";

		public ConfigEntry<bool> Enabled { get; }

		public ConfigEntry<float> BaseDropChance { get; }

		public ConfigEntry<float> DropChanceMultiplier { get; }

		public ConfigEntry<float> MaxDropChance { get; }

		public ConfigEntry<bool> VerboseLogging { get; }

		public ConfigEntry<float> EliteMultiplier { get; }

		public ConfigEntry<float> BossMultiplier { get; }

		public ConfigEntry<bool> StageScalingEnabled { get; }

		public ConfigEntry<float> BonusPerStage { get; }

		public ConfigEntry<float> MaxStageBonus { get; }

		public ConfigEntry<bool> PityEnabled { get; }

		public ConfigEntry<int> KillsBeforeGuaranteedDrop { get; }

		public ConfigEntry<bool> JackpotEnabled { get; }

		public ConfigEntry<float> JackpotChance { get; }

		public ConfigEntry<int> JackpotItemCount { get; }

		public PluginConfig(ConfigFile file)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Expected O, but got Unknown
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Expected O, but got Unknown
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Expected O, but got Unknown
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Expected O, but got Unknown
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Expected O, but got Unknown
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Expected O, but got Unknown
			Enabled = file.Bind<bool>("1. General", "Enabled", true, "Enable the mod. When disabled, Sacrifice uses the vanilla drop logic.");
			BaseDropChance = file.Bind<float>("1. General", "Base Drop Chance", 5f, new ConfigDescription("Base drop chance in percent before any multiplier. Vanilla is 5.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			DropChanceMultiplier = file.Bind<float>("1. General", "Drop Chance Multiplier", 2f, new ConfigDescription("Multiplier applied to the base drop chance. 1 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			MaxDropChance = file.Bind<float>("1. General", "Max Drop Chance", 100f, new ConfigDescription("Hard cap in percent on the final drop chance, after every bonus.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			VerboseLogging = file.Bind<bool>("1. General", "Verbose Logging", false, "Log every Sacrifice roll (monster, chance, result) to the BepInEx console. For troubleshooting.");
			EliteMultiplier = file.Bind<float>("2. Elites and Bosses", "Elite Multiplier", 1.5f, new ConfigDescription("Extra multiplier for elite monsters. 1 = no bonus.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			BossMultiplier = file.Bind<float>("2. Elites and Bosses", "Boss Multiplier", 2f, new ConfigDescription("Extra multiplier for bosses and champions. Stacks with the elite multiplier. 1 = no bonus.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			StageScalingEnabled = file.Bind<bool>("3. Stage Scaling", "Enabled", true, "Increase the drop chance with every stage cleared.");
			BonusPerStage = file.Bind<float>("3. Stage Scaling", "Bonus Per Stage", 10f, new ConfigDescription("Drop chance bonus in percent per stage cleared (10 = +10% of the chance per stage).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			MaxStageBonus = file.Bind<float>("3. Stage Scaling", "Max Stage Bonus", 100f, new ConfigDescription("Maximum total stage bonus in percent (100 = chance doubled at most).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>()));
			PityEnabled = file.Bind<bool>("4. Pity", "Enabled", true, "Guarantee a drop after a streak of eligible kills without any drop.");
			KillsBeforeGuaranteedDrop = file.Bind<int>("4. Pity", "Kills Before Guaranteed Drop", 40, new ConfigDescription("Number of consecutive kills without a drop before the next kill is guaranteed to drop.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			JackpotEnabled = file.Bind<bool>("5. Jackpot", "Enabled", true, "Give each drop a small chance to burst into several items.");
			JackpotChance = file.Bind<float>("5. Jackpot", "Jackpot Chance", 2f, new ConfigDescription("Chance in percent that a drop becomes a jackpot.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			JackpotItemCount = file.Bind<int>("5. Jackpot", "Jackpot Item Count", 3, new ConfigDescription("Number of items dropped by a jackpot.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 20), Array.Empty<object>()));
		}
	}
}
namespace ExtendedSacrifice.Compatibility
{
	internal static class RiskOfOptionsCompat
	{
		public const string PluginGUID = "com.rune580.riskofoptions";

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static void Register(PluginConfig config)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Expected O, but got Unknown
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Expected O, but got Unknown
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Expected O, but got Unknown
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: 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_0168: Expected O, but got Unknown
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Expected O, but got Unknown
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Expected O, but got Unknown
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Expected O, but got Unknown
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("More item drops from monsters when the Artifact of Sacrifice is active.\n\nMultiplayer: only the host needs the mod, and only the host's settings apply.");
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(config.Enabled));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(config.BaseDropChance, Slider(0f, 100f, "{0:0.#}%")));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(config.DropChanceMultiplier, Slider(0f, 20f, "x{0:0.##}")));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(config.MaxDropChance, Slider(0f, 100f, "{0:0.#}%")));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(config.VerboseLogging));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(config.EliteMultiplier, Slider(0f, 10f, "x{0:0.##}")));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(config.BossMultiplier, Slider(0f, 10f, "x{0:0.##}")));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(config.StageScalingEnabled));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(config.BonusPerStage, Slider(0f, 100f, "+{0:0.#}%")));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(config.MaxStageBonus, Slider(0f, 1000f, "+{0:0}%")));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(config.PityEnabled));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(config.KillsBeforeGuaranteedDrop, new IntSliderConfig
			{
				min = 1,
				max = 500
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(config.JackpotEnabled));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(config.JackpotChance, Slider(0f, 100f, "{0:0.#}%")));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(config.JackpotItemCount, new IntSliderConfig
			{
				min = 2,
				max = 20
			}));
		}

		private static SliderConfig Slider(float min, float max, string format)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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_001b: Expected O, but got Unknown
			return new SliderConfig
			{
				min = min,
				max = max,
				FormatString = format
			};
		}
	}
}