Decompiled source of EvenBetterExponentialItems v1.5.1

plugins\EvenBetterExponentialItems\EvenBetterExponentialItems.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
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 Microsoft.CodeAnalysis;
using On.RoR2;
using R2API;
using RoR2;
using UnityEngine;

[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("EvenBetterExponentialItems")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1")]
[assembly: AssemblyProduct("Even Better Exponential Items")]
[assembly: AssemblyTitle("EvenBetterExponentialItems")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.1.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 EvenBetterExponentialItems
{
	internal static class BuildPresetManager
	{
		public static void Apply(ExponentialItemsConfig config)
		{
			switch (config.ActivePreset.Value)
			{
			case ScalingPreset.VanillaPlus:
				config.DefaultScalingMode.Value = ScalingMode.Linear;
				config.GlobalGrowth.Value = 1.05;
				break;
			case ScalingPreset.Chaos:
				config.DefaultScalingMode.Value = ScalingMode.Exponential;
				config.GlobalGrowth.Value = 1.4;
				break;
			case ScalingPreset.ProcHell:
				config.DefaultScalingMode.Value = ScalingMode.Exponential;
				config.ProcMultiplier.Value = 1.6;
				config.MaxProcDepth.Value = 6;
				break;
			case ScalingPreset.MMOScaling:
				config.DefaultScalingMode.Value = ScalingMode.Logarithmic;
				config.GlobalGrowth.Value = 1.2;
				config.SoftCapTarget.Value = 300.0;
				break;
			case ScalingPreset.Hardcore:
				config.DefaultScalingMode.Value = ScalingMode.Hyperbolic;
				config.GlobalGrowth.Value = 0.7;
				config.SoftCapTarget.Value = 70.0;
				break;
			case ScalingPreset.MovementDisabled:
				config.MovementMultiplier.Value = 0.0;
				break;
			case ScalingPreset.Custom:
				break;
			}
		}
	}
	internal sealed class CategoryRegistry
	{
		public ItemCategory Resolve(ItemDef itemDef)
		{
			//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_0033: Expected I4, but got Unknown
			if ((Object)(object)itemDef == (Object)null)
			{
				return ItemCategory.Utility;
			}
			ItemTag[] tags = itemDef.tags;
			if (tags != null)
			{
				foreach (ItemTag val in tags)
				{
					switch (val - 1)
					{
					case 1:
						return ItemCategory.Healing;
					case 2:
						return ItemCategory.Utility;
					case 0:
						return ItemCategory.Damage;
					}
				}
			}
			string text = ((Object)itemDef).name ?? string.Empty;
			if (text.IndexOf("Syringe", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return ItemCategory.Damage;
			}
			if (VanillaUnsafeItemCatalog.TryGetProfile(itemDef, out var profile) && (profile.Flags & UnsafeBehaviorFlags.Movement) != 0)
			{
				return ItemCategory.Movement;
			}
			if (text.IndexOf("Hoof", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Feather", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Quail", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return ItemCategory.Movement;
			}
			if (text.IndexOf("Bandolier", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("AlienHead", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return ItemCategory.Cooldown;
			}
			if (text.IndexOf("Proc", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Missile", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return ItemCategory.Proc;
			}
			if (text.IndexOf("Armor", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Shield", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("TougherTimes", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return ItemCategory.Defense;
			}
			return ItemCategory.Damage;
		}
	}
	internal static class ConfigLimits
	{
		public const int AbsoluteStackCeiling = 536870911;

		public const int RiskOfOptionsSliderMax = 536870911;
	}
	[BepInPlugin("com.entersackman.evenbetterexponentialitems", "Even Better Exponential Items", "1.5.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class EvenBetterExponentialItemsPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.entersackman.evenbetterexponentialitems";

		public const string PluginName = "Even Better Exponential Items";

		public const string PluginVersion = "1.5.1";

		private static ArtifactDef exponentialArtifactDef;

		private static EvenBetterExponentialItemsPlugin _instance;

		private ExponentialItemsConfig _config;

		private ItemScalingManager _scalingManager;

		private TooltipManager _tooltipManager;

		private readonly Dictionary<string, ItemDef> _itemByDescToken = new Dictionary<string, ItemDef>(StringComparer.Ordinal);

		private static bool suppressExponentialHook;

		private string PluginDirectory => Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? string.Empty;

		private void Awake()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			_instance = this;
			_config = new ExponentialItemsConfig(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
			BuildPresetManager.Apply(_config);
			UnsafeItemRegistry unsafeRegistry = new UnsafeItemRegistry(_config);
			_scalingManager = new ItemScalingManager(_config, new ScalingFormulaRegistry(), new CategoryRegistry(), unsafeRegistry, new ProcSafetyManager(_config));
			_tooltipManager = new TooltipManager(_config, _scalingManager, unsafeRegistry);
			RegisterArtifact();
			Inventory.GiveItem_ItemIndex_int += new hook_GiveItem_ItemIndex_int(Inventory_GiveItem_ItemIndex_int);
			Inventory.GiveItem_ItemDef_int += new hook_GiveItem_ItemDef_int(Inventory_GiveItem_ItemDef_int);
			GenericPickupController.AttemptGrant += new hook_AttemptGrant(GenericPickupController_AttemptGrant);
			Language.GetLocalizedStringByToken += new hook_GetLocalizedStringByToken(Language_GetLocalizedStringByToken);
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Artifact registered: {2}", "Even Better Exponential Items", "1.5.1", (Object)(object)exponentialArtifactDef != (Object)null));
			RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(OnRoR2CatalogReady));
			if (_config.DebugLogArtifactList.Value)
			{
				RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(LogLoadedArtifacts));
			}
			_config.TryRebuildFromCatalog("Awake post-bind");
			RiskOfOptionsIntegration.TryRegister(((BaseUnityPlugin)this).Logger, _config);
		}

		private void OnRoR2CatalogReady()
		{
			_config.RebuildFromCatalog("RoR2Application.onLoad (item catalog ready)");
			RebuildTooltipCache();
			RiskOfOptionsIntegration.TryRegister(((BaseUnityPlugin)this).Logger, _config);
		}

		private void RebuildTooltipCache()
		{
			_itemByDescToken.Clear();
			int itemCount = ItemCatalog.itemCount;
			for (int i = 0; i < itemCount; i++)
			{
				ItemDef itemDef = ItemCatalog.GetItemDef((ItemIndex)i);
				if ((Object)(object)itemDef != (Object)null && !string.IsNullOrEmpty(itemDef.descriptionToken))
				{
					_itemByDescToken[itemDef.descriptionToken] = itemDef;
				}
			}
		}

		private void RegisterArtifact()
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			LanguageAPI.Add("ARTIFACT_EXPONENTIALITEMS_NAME", "Artifact of Exponents");
			LanguageAPI.Add("ARTIFACT_EXPONENTIALITEMS_DESC", "Items stack exponentially instead of one at a time.");
			exponentialArtifactDef = ScriptableObject.CreateInstance<ArtifactDef>();
			exponentialArtifactDef.cachedName = "ExponentialItems";
			exponentialArtifactDef.nameToken = "ARTIFACT_EXPONENTIALITEMS_NAME";
			exponentialArtifactDef.descriptionToken = "ARTIFACT_EXPONENTIALITEMS_DESC";
			Texture2D val = TryLoadTexture("icon_selected.png", "icon.png");
			Texture2D val2 = TryLoadTexture("icon_deselected.png");
			Sprite smallIconSelectedSprite = (((Object)(object)val != (Object)null) ? CreateSpriteFromTexture(val, "artifact_selected") : CreateFallbackIcon(new Color(0.25f, 0.8f, 0.35f, 1f), "fallback_selected"));
			Sprite smallIconDeselectedSprite = (((Object)(object)val2 != (Object)null) ? CreateSpriteFromTexture(val2, "artifact_deselected") : ((!((Object)(object)val != (Object)null)) ? CreateFallbackIcon(new Color(0.2f, 0.2f, 0.2f, 1f), "fallback_deselected") : CreateSpriteFromTexture(CreateDarkenedCopy(val, 0.55f), "artifact_deselected_dim")));
			exponentialArtifactDef.smallIconSelectedSprite = smallIconSelectedSprite;
			exponentialArtifactDef.smallIconDeselectedSprite = smallIconDeselectedSprite;
			ContentAddition.AddArtifactDef(exponentialArtifactDef);
		}

		private Texture2D TryLoadTexture(params string[] fileNamesInOrder)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			foreach (string text in fileNamesInOrder)
			{
				string text2 = Path.Combine(PluginDirectory, text);
				if (!File.Exists(text2))
				{
					continue;
				}
				try
				{
					byte[] array = File.ReadAllBytes(text2);
					Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
					if (!ImageConversion.LoadImage(val, array))
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Icon file could not be decoded: " + text2));
						continue;
					}
					((Object)val).name = Path.GetFileNameWithoutExtension(text);
					return val;
				}
				catch (Exception arg)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to load icon {text}: {arg}");
				}
			}
			return null;
		}

		private static Sprite CreateSpriteFromTexture(Texture2D texture, string spriteName)
		{
			//IL_0020: 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)
			((Object)texture).name = spriteName;
			return Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), new Vector2(0.5f, 0.5f), 100f);
		}

		private static Texture2D CreateDarkenedCopy(Texture2D source, float rgbMultiplier)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_0042: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			int width = ((Texture)source).width;
			int height = ((Texture)source).height;
			Texture2D val = new Texture2D(width, height, (TextureFormat)4, false);
			((Object)val).name = ((Object)source).name + "_dark";
			Color[] pixels = source.GetPixels();
			float num = Mathf.Clamp01(rgbMultiplier);
			for (int i = 0; i < pixels.Length; i++)
			{
				Color val2 = pixels[i];
				pixels[i] = new Color(val2.r * num, val2.g * num, val2.b * num, val2.a);
			}
			val.SetPixels(pixels);
			val.Apply();
			return val;
		}

		private Sprite CreateFallbackIcon(Color color, string name)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false);
				Color[] array = (Color[])(object)new Color[16384];
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = color;
				}
				val.SetPixels(array);
				val.Apply();
				((Object)val).name = name;
				return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to create fallback icon: " + ex));
				return null;
			}
		}

		private void Inventory_GiveItem_ItemIndex_int(orig_GiveItem_ItemIndex_int orig, Inventory self, ItemIndex itemIndex, int count)
		{
			//IL_00e1: 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_0009: 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_00be: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (suppressExponentialHook || !ShouldApply(self, itemIndex, count, out var itemDef))
				{
					orig.Invoke(self, itemIndex, count);
					return;
				}
				int itemCountPermanent = self.GetItemCountPermanent(itemIndex);
				int num = _scalingManager.ResolveGrantedAmount(itemDef, itemCountPermanent);
				ScalingPlan scalingPlan = _scalingManager.BuildPlan(itemDef, itemCountPermanent);
				LogDebug($"GiveItem {Describe(itemDef)}: mode={scalingPlan.Mode}, category={scalingPlan.Category}, current={scalingPlan.CurrentStack}, target={scalingPlan.TargetStack}, adding={num}, multiplier={scalingPlan.EffectiveMultiplier:F2}");
				suppressExponentialHook = true;
				try
				{
					orig.Invoke(self, itemIndex, num);
				}
				finally
				{
					suppressExponentialHook = false;
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
				orig.Invoke(self, itemIndex, count);
			}
		}

		private void Inventory_GiveItem_ItemDef_int(orig_GiveItem_ItemDef_int orig, Inventory self, ItemDef itemDef, int count)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0044: 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_005c: Expected O, but got Unknown
			if ((Object)itemDef == (Object)null)
			{
				orig.Invoke(self, itemDef, count);
				return;
			}
			Inventory_GiveItem_ItemIndex_int((orig_GiveItem_ItemIndex_int)delegate(Inventory inventory, ItemIndex itemIndex, int itemCount)
			{
				orig.Invoke(inventory, itemDef, itemCount);
			}, self, itemDef.itemIndex, count);
		}

		private void GenericPickupController_AttemptGrant(orig_AttemptGrant orig, GenericPickupController self, CharacterBody body)
		{
			//IL_0014: 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_002b: 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_003a: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Invalid comparison between Unknown and I4
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Invalid comparison between Unknown and I4
			//IL_0067: 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_007c: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			Inventory val = (((Object)(object)body != (Object)null) ? body.inventory : null);
			PickupDef pickupDef = PickupCatalog.GetPickupDef(self.pickupIndex);
			ItemIndex val2 = (ItemIndex)((pickupDef == null) ? (-1) : ((int)pickupDef.itemIndex));
			int num = 0;
			if ((Object)val != (Object)null && (int)val2 != -1)
			{
				num = val.GetItemCountPermanent(val2);
			}
			orig.Invoke(self, body);
			try
			{
				if ((Object)val == (Object)null || (int)val2 == -1)
				{
					return;
				}
				ItemDef itemDef = ItemCatalog.GetItemDef(val2);
				if ((Object)itemDef == (Object)null || suppressExponentialHook || !ShouldApplyItem(val, val2))
				{
					return;
				}
				int itemCountPermanent = val.GetItemCountPermanent(val2);
				if (itemCountPermanent <= num)
				{
					return;
				}
				ScalingPlan scalingPlan = _scalingManager.BuildPlan(itemDef, num);
				if (scalingPlan.TargetStack <= itemCountPermanent)
				{
					return;
				}
				int num2 = Math.Max(1, scalingPlan.TargetStack - itemCountPermanent);
				if (num2 <= 0)
				{
					return;
				}
				LogDebug($"Pickup {Describe(itemDef)}: mode={scalingPlan.Mode}, category={scalingPlan.Category}, before={num}, after={itemCountPermanent}, target={scalingPlan.TargetStack}, adding={num2}");
				suppressExponentialHook = true;
				try
				{
					val.GiveItemPermanent(val2, num2);
				}
				finally
				{
					suppressExponentialHook = false;
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
			}
		}

		private bool ShouldApply(Inventory self, ItemIndex itemIndex, int count, out ItemDef itemDef)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			itemDef = null;
			if (count != 1)
			{
				return false;
			}
			if (!ShouldApplyItem(self, itemIndex, out itemDef))
			{
				return false;
			}
			return true;
		}

		private bool ShouldApplyItem(Inventory self, ItemIndex itemIndex)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			ItemDef itemDef;
			return ShouldApplyItem(self, itemIndex, out itemDef);
		}

		private bool ShouldApplyItem(Inventory self, ItemIndex itemIndex, out ItemDef itemDef)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			itemDef = null;
			if ((Object)self == (Object)null || (int)itemIndex == -1)
			{
				return false;
			}
			if ((Object)exponentialArtifactDef == (Object)null)
			{
				return false;
			}
			if (_config.RequireArtifactEnabled.Value)
			{
				if ((Object)RunArtifactManager.instance == (Object)null)
				{
					return false;
				}
				if (!RunArtifactManager.instance.IsArtifactEnabled(exponentialArtifactDef))
				{
					return false;
				}
			}
			ItemDef itemDef2 = ItemCatalog.GetItemDef(itemIndex);
			if ((Object)itemDef2 == (Object)null)
			{
				return false;
			}
			if (!_scalingManager.ShouldScaleItem(itemDef2))
			{
				if (_config.DebugLogging.Value)
				{
					LogDebug("Scaling disabled by registry rules: " + Describe(itemDef2));
				}
				return false;
			}
			itemDef = itemDef2;
			return true;
		}

		private string Language_GetLocalizedStringByToken(orig_GetLocalizedStringByToken orig, Language self, string token)
		{
			string text = orig.Invoke(self, token);
			if (!_config.EnableTooltipInfo.Value || string.IsNullOrEmpty(token) || !token.EndsWith("_DESC", StringComparison.Ordinal))
			{
				return text;
			}
			ItemDef val = FindItemDefForDescToken(token);
			if ((Object)(object)val == (Object)null || !_scalingManager.ShouldScaleItem(val))
			{
				return text;
			}
			return text + _tooltipManager.BuildTooltipSuffix(val, 1);
		}

		private ItemDef FindItemDefForDescToken(string token)
		{
			if (_itemByDescToken.Count == 0)
			{
				RebuildTooltipCache();
			}
			if (!_itemByDescToken.TryGetValue(token, out var value))
			{
				return null;
			}
			return value;
		}

		private void LogLoadedArtifacts()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Total Artifacts Loaded: {ArtifactCatalog.artifactCount}");
			for (int i = 0; i < ArtifactCatalog.artifactCount; i++)
			{
				ArtifactDef artifactDef = ArtifactCatalog.GetArtifactDef((ArtifactIndex)i);
				if ((Object)artifactDef != (Object)null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Artifact: " + artifactDef.cachedName));
				}
			}
		}

		private void LogDebug(string message)
		{
			if (_config != null && _config.DebugLogging.Value)
			{
				Debug.Log((object)("[Even Better Exponential Items] " + message));
			}
		}

		private static string Describe(ItemDef def)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected I4, but got Unknown
			if ((Object)def == (Object)null)
			{
				return "(null ItemDef)";
			}
			return $"{((Object)def).name} (index={(int)def.itemIndex})";
		}
	}
	public enum ScalingMode
	{
		Linear,
		Exponential,
		Logarithmic,
		Hyperbolic,
		SoftCap,
		Vanilla
	}
	public enum ScalingPreset
	{
		Custom,
		VanillaPlus,
		Chaos,
		ProcHell,
		MMOScaling,
		Hardcore,
		MovementDisabled
	}
	public enum ItemCategory
	{
		Damage,
		Defense,
		Healing,
		Utility,
		Proc,
		Movement,
		Cooldown
	}
	public sealed class ItemScalingOverride
	{
		public ScalingMode Mode;

		public double Growth;

		public double Cap;
	}
	public sealed class ExponentialItemsConfig
	{
		public const string SectionGeneralScaling = "General Scaling";

		public const string SectionItemFilters = "Item Filters";

		public const string SectionLists = "Lists & Blacklist";

		public const string SectionCategoryInfluence = "Category Influence";

		public const string SectionRiskyItems = "Risky Items";

		public const string SectionProcCombat = "Proc & Combat Limits";

		public const string SectionTooltips = "Tooltips";

		public const string SectionAdvanced = "Advanced";

		public const string SectionDebug = "Debug";

		public readonly ConfigEntry<bool> RequireArtifactEnabled;

		public readonly ConfigEntry<int> MaxStack;

		public readonly ConfigEntry<int> BaseSize;

		public readonly ConfigEntry<ScalingMode> DefaultScalingMode;

		public readonly ConfigEntry<double> GlobalGrowth;

		public readonly ConfigEntry<double> SoftCapTarget;

		public readonly ConfigEntry<ScalingPreset> ActivePreset;

		public readonly ConfigEntry<bool> EnablePerItemOverrides;

		public readonly ConfigEntry<bool> AffectTier1;

		public readonly ConfigEntry<bool> AffectTier2;

		public readonly ConfigEntry<bool> AffectTier3;

		public readonly ConfigEntry<bool> AffectBoss;

		public readonly ConfigEntry<bool> AffectLunar;

		public readonly ConfigEntry<bool> AffectVoid;

		public readonly ConfigEntry<bool> AffectTierless;

		public readonly ConfigEntry<bool> AffectTemporaryItems;

		public readonly ConfigEntry<string> BlacklistedItemsRaw;

		public readonly ConfigEntry<bool> WhitelistMode;

		public readonly ConfigEntry<string> WhitelistItemsRaw;

		public readonly ConfigEntry<string> CategoryBlacklistRaw;

		public readonly ConfigEntry<string> PerItemScalingOverridesRaw;

		public readonly ConfigEntry<string> PerItemStackOverridesRaw;

		public readonly ConfigEntry<double> MovementMultiplier;

		public readonly ConfigEntry<double> DefenseMultiplier;

		public readonly ConfigEntry<double> ProcMultiplier;

		public readonly ConfigEntry<double> HealingMultiplier;

		public readonly ConfigEntry<double> UtilityMultiplier;

		public readonly ConfigEntry<double> CooldownMultiplier;

		public readonly ConfigEntry<double> DamageMultiplier;

		public readonly ConfigEntry<double> CategoryExponent;

		public readonly ConfigEntry<string> UnsafeItemsRaw;

		public readonly ConfigEntry<bool> UnsafeUseManualList;

		public readonly ConfigEntry<bool> UseVanillaUnsafeCatalog;

		public readonly ConfigEntry<UnsafeCatalogSensitivity> RiskCatalogSensitivity;

		public readonly ConfigEntry<bool> UnsafeRespectCatalogScalingModes;

		public readonly ConfigEntry<bool> UnsafeAutomaticLegacyPatterns;

		public readonly ConfigEntry<ScalingMode> UnsafeDefaultMode;

		public readonly ConfigEntry<bool> UnsafeUseDiminishingReturns;

		public readonly ConfigEntry<double> UnsafeSoftCapTarget;

		public readonly ConfigEntry<int> UnsafeHardCap;

		public readonly ConfigEntry<bool> TooltipShowRiskAudit;

		public readonly ConfigEntry<int> MaxProcDepth;

		public readonly ConfigEntry<int> MaxProjectilesPerFrame;

		public readonly ConfigEntry<float> MaxVelocity;

		public readonly ConfigEntry<float> MaxAttackSpeed;

		public readonly ConfigEntry<double> MaxDamageCoefficient;

		public readonly ConfigEntry<bool> EnableTooltipInfo;

		public readonly ConfigEntry<bool> LiveReloadLists;

		public readonly ConfigEntry<bool> DeterministicRounding;

		public readonly ConfigEntry<bool> DebugLogging;

		public readonly ConfigEntry<bool> DebugLogArtifactList;

		public readonly ConfigEntry<bool> DebugLogCatalogRebuild;

		private readonly ManualLogSource _log;

		private readonly HashSet<string> _warnedTokens = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<int> _blacklistedIndices = new HashSet<int>();

		private readonly HashSet<int> _whitelistedIndices = new HashSet<int>();

		private readonly HashSet<ItemCategory> _categoryBlacklist = new HashSet<ItemCategory>();

		private readonly HashSet<int> _manualExtraUnsafeIndices = new HashSet<int>();

		private readonly Dictionary<int, int> _stackCapByItemIndex = new Dictionary<int, int>();

		private readonly Dictionary<int, ItemScalingOverride> _scalingOverridesByItem = new Dictionary<int, ItemScalingOverride>();

		public ExponentialItemsConfig(ConfigFile config, ManualLogSource log)
		{
			_log = log ?? throw new ArgumentNullException("log");
			RequireArtifactEnabled = config.Bind<bool>("General Scaling", "Require Exponents Artifact", true, "When ON, this mod only changes how items stack while the Artifact of Exponents is enabled for the run. Turn OFF if you want faster stacking without enabling that artifact.");
			MaxStack = config.Bind<int>("General Scaling", "Maximum Stack Count", 4096, "Absolute ceiling on stack count after this mod's math runs (unless a per-item cap in \"Per-Item Stack Hard Caps\" is lower). Maximum ~536,870,911 (int.MaxValue / 4) for math safety.");
			BaseSize = config.Bind<int>("General Scaling", "Exponential Base Size", 2, "Exponent base used by Exponential mode only (minimum 2). Higher = bigger jumps between milestones.");
			DefaultScalingMode = config.Bind<ScalingMode>("General Scaling", "Default Scaling Mode", ScalingMode.Exponential, "Default growth curve for items that are not \"risky\" and do not have a per-item override.\n\nVanilla — Same as Linear (+1 per pickup).\n\nLinear — +1 stack per qualifying pickup.\n\nExponential — Jumps toward milestones (uses Exponential Base Size).\n\nLogarithmic — Gains based on log curve × Global Growth (usually gentler late run).\n\nSoftCap — Fast early growth that eases toward a plateau (uses Soft Cap Plateau Target).\n\nHyperbolic — Asymptotic curve; good for toned-down scaling.\n\nTip: With Deterministic +1 Pickup Rounding ON, every valid pickup still moves stacks forward by at least +1.");
			GlobalGrowth = config.Bind<double>("General Scaling", "Global Growth Multiplier", 1.12, "Multiplier baked into formulas (especially Logarithmic and category-adjusted growth). Typical ~1.0–1.4.");
			SoftCapTarget = config.Bind<double>("General Scaling", "Soft Cap Plateau Target", 200.0, "Default \"plateau width\" for SoftCap and Hyperbolic when an item does not set its own cap in overrides. Higher = higher stacks before diminishing kicks in hard.");
			ActivePreset = config.Bind<ScalingPreset>("General Scaling", "Balance Preset", ScalingPreset.Custom, "Startup preset that adjusts several numbers when you launch (you can still tweak after).\n\nCustom — No preset changes.\n\nVanillaPlus — Linear-ish, mild growth.\n\nChaos — Exponential with high growth.\n\nProcHell — Exponential with higher proc strength (still has proc depth guard).\n\nMMOScaling — Logarithmic-leaning with a roomy soft cap.\n\nHardcore — Hyperbolic with a tighter soft cap.\n\nMovementDisabled — Sets Movement Item Strength to 0 so movement buckets stop influencing growth.");
			EnablePerItemOverrides = config.Bind<bool>("General Scaling", "Enable Per-Item Override Rules", true, "When ON, the \"Per-Item Scaling Rules\" string is parsed. Turn OFF to ignore those rules without deleting them.");
			AffectTier1 = config.Bind<bool>("Item Filters", "Scale White (Common) Items", true, "Allow white (common) items to use accelerated stacking.");
			AffectTier2 = config.Bind<bool>("Item Filters", "Scale Green (Uncommon) Items", true, "Allow green (uncommon) items.");
			AffectTier3 = config.Bind<bool>("Item Filters", "Scale Red (Legendary) Items", true, "Allow red (legendary) items.");
			AffectBoss = config.Bind<bool>("Item Filters", "Scale Boss (Yellow) Items", true, "Allow boss (yellow) items.");
			AffectLunar = config.Bind<bool>("Item Filters", "Scale Lunar Items", true, "Allow lunar items.");
			AffectVoid = config.Bind<bool>("Item Filters", "Scale Void Items", true, "Allow void-tier items.");
			AffectTierless = config.Bind<bool>("Item Filters", "Scale Hidden / No-Tier Items", false, "Allow hidden/no-tier items (risky; off by default).");
			AffectTemporaryItems = config.Bind<bool>("Item Filters", "Scale Temporary Runtime Items", false, "Allow runtime temporary items (usually off).");
			BlacklistedItemsRaw = config.Bind<string>("Lists & Blacklist", "Items: Skip Scaling Entirely", string.Empty, "Internal item code names (comma or semicolon), NOT display titles. Those items never use this mod's accelerated stacking (always +1). Leave empty if you only want to tame items via the Risk Catalog instead.");
			WhitelistMode = config.Bind<bool>("Lists & Blacklist", "Whitelist Mode (Only Listed Items Scale)", false, "When ON, ONLY items listed under Whitelist Item Codes get accelerated stacking; everything else stays vanilla +1 for this mod.");
			WhitelistItemsRaw = config.Bind<string>("Lists & Blacklist", "Whitelist Item Codes", string.Empty, "Same format as blacklist. Only used when Whitelist Mode is ON.");
			CategoryBlacklistRaw = config.Bind<string>("Lists & Blacklist", "Category Blacklist", string.Empty, "Skip entire categories from accelerated stacking. Tokens (case-insensitive): Damage, Defense, Healing, Utility, Proc, Movement, Cooldown.\n\nExample: Movement,Proc");
			PerItemScalingOverridesRaw = config.Bind<string>("Lists & Blacklist", "Per-Item Scaling Rules (Advanced)", string.Empty, "Advanced: ItemCode.Mode=SoftCap; ItemCode.Growth=1.1; ItemCode.Cap=120 — uses internal names (e.g. LensMakersGlasses).");
			PerItemStackOverridesRaw = config.Bind<string>("Lists & Blacklist", "Per-Item Stack Hard Caps", string.Empty, "Advanced: ItemCode=256; OtherItem=64 — hard caps per item after scaling math.");
			MovementMultiplier = config.Bind<double>("Category Influence", "Movement Item Strength", 0.35, "How strongly Movement-tagged items influence growth. Lower = less runaway speed scaling from the category system; 0 matches Movement Disabled preset for this bucket.");
			DefenseMultiplier = config.Bind<double>("Category Influence", "Defense Item Strength", 0.8, "Strength multiplier for Defense-like items.");
			ProcMultiplier = config.Bind<double>("Category Influence", "Proc Item Strength", 1.25, "Strength for proc/on-hit items. Raising this can worsen lag in extreme builds.");
			HealingMultiplier = config.Bind<double>("Category Influence", "Healing Item Strength", 1.0, "Strength for healing-related items.");
			UtilityMultiplier = config.Bind<double>("Category Influence", "Utility Item Strength", 1.0, "Strength for broad utility items.");
			CooldownMultiplier = config.Bind<double>("Category Influence", "Cooldown Item Strength", 0.65, "Strength for cooldown-reduction style items.");
			DamageMultiplier = config.Bind<double>("Category Influence", "Damage Item Strength", 1.0, "Strength for damage-focused items.");
			CategoryExponent = config.Bind<double>("Category Influence", "Category Influence Exponent", 1.0, "Exponent on category multipliers blended with Global Growth — higher = category dials matter more.");
			UseVanillaUnsafeCatalog = config.Bind<bool>("Risky Items", "Use Built-In Vanilla Risk Catalog", true, "When ON (recommended), a built-in table classifies vanilla + DLC items by gameplay/systems risk for exponential stacking. Risky items use \"Fallback Risky Scaling Mode\" and optional tighter soft caps unless you override them per item.");
			RiskCatalogSensitivity = config.Bind<UnsafeCatalogSensitivity>("Risky Items", "Risk Catalog Sensitivity", UnsafeCatalogSensitivity.UnsafeAndAbove, "How many catalog tiers count as \"risky\" for scaling:\n\nExtremeOnly — Only the worst offenders (Gesture, Clover, Lysate, etc.).\n\nUnsafeAndAbove — Default. All Unsafe + Extreme catalog entries.\n\nMildAndAbove — Also includes Mild (crit glasses, many heal/regen items). More items use the risky curve.");
			UnsafeRespectCatalogScalingModes = config.Bind<bool>("Risky Items", "Use Catalog Recommended Scaling Modes", false, "When ON, risky items use each catalog entry's recommended curve (SoftCap, Hyperbolic, HardCap) instead of always using Fallback Risky Scaling Mode. Advanced — can feel very different from the global default.");
			UnsafeUseManualList = config.Bind<bool>("Risky Items", "Use Additional Manual Risk List", false, "When ON, parses \"Additional Risky Item Codes\" and merges those items into the risky list. When OFF, that text is ignored.");
			UnsafeItemsRaw = config.Bind<string>("Risky Items", "Additional Risky Item Codes", string.Empty, "Optional extra internal names to ALWAYS treat as risky (merged on top of the catalog). Only read when \"Use Additional Manual Risk List\" is ON.");
			UnsafeAutomaticLegacyPatterns = config.Bind<bool>("Risky Items", "Legacy Name Pattern Detection", false, "DANGEROUS for mod compatibility: substring match on internal names (Hoof, Feather, Quail, Syringe, Times). Prefer the built-in catalog for vanilla. Turn ON only if you need fuzzy matching for custom items.");
			UnsafeDefaultMode = config.Bind<ScalingMode>("Risky Items", "Fallback Risky Scaling Mode", ScalingMode.SoftCap, "Scaling mode for risky items when \"Use Catalog Recommended Scaling Modes\" is OFF (or the item has no catalog mode). Default SoftCap is vanilla-friendly.");
			UnsafeUseDiminishingReturns = config.Bind<bool>("Risky Items", "Tighter Soft Cap For Risky Items", true, "When ON, risky items use the tighter \"Risky Items Soft Cap Target\" for plateau width unless a per-item override sets Cap.");
			UnsafeSoftCapTarget = config.Bind<double>("Risky Items", "Risky Items Soft Cap Target", 80.0, "Plateau width for risky items when Tighter Soft Cap is ON — usually lower than the global Soft Cap Plateau Target.");
			UnsafeHardCap = config.Bind<int>("Risky Items", "Global Stack Hard Cap (Safety)", 512, "Safety valve: maximum stacks after scaling math (applies to all items through the proc clamp path). Same absolute ceiling as Maximum Stack Count (~536,870,911).");
			MaxProcDepth = config.Bind<int>("Proc & Combat Limits", "Maximum Proc Chain Depth", 8, "Maximum proc chain depth the mod uses when clamping huge single-frame grants. Lower = safer CPU, stricter anti-spam.");
			MaxProjectilesPerFrame = config.Bind<int>("Proc & Combat Limits", "Max Items Granted Per Frame", 120, "Caps how many stacks can be granted in one frame from a single accelerated pickup burst — prevents single-frame spikes.");
			MaxVelocity = config.Bind<float>("Proc & Combat Limits", "Velocity Clamp (Guardrail)", 450f, "Reference guardrail value paired with movement/proc tuning in config (full combat clamping depends on other mods/hooks).");
			MaxAttackSpeed = config.Bind<float>("Proc & Combat Limits", "Attack Speed Clamp (Guardrail)", 15f, "Reference attack speed ceiling used in the mod's safety tuning metadata.");
			MaxDamageCoefficient = config.Bind<double>("Proc & Combat Limits", "Damage Coefficient Clamp (Guardrail)", 100000.0, "Very high damage coefficient ceiling used as a numerical guardrail in scaling safety.");
			EnableTooltipInfo = config.Bind<bool>("Tooltips", "Show Scaling Hints On Items", true, "When ON, item descriptions can show short lines about scaling mode and estimated gains (informational).");
			TooltipShowRiskAudit = config.Bind<bool>("Tooltips", "Show Risk Audit Tags On Items", false, "When ON (and tooltips are ON), also shows catalog risk tier, behavior flags, and suggested hard cap hints for audited items. For advanced users.");
			LiveReloadLists = config.Bind<bool>("Advanced", "Live Reload Lists From Disk", true, "When ON, editing the cfg file can refresh blacklist / overrides without restarting (when the game is in a good state).");
			DeterministicRounding = config.Bind<bool>("Advanced", "Deterministic +1 Pickup Rounding", true, "When ON, each qualifying pickup still moves stacks by at least +1 toward the computed target.");
			DebugLogging = config.Bind<bool>("Debug", "Verbose Debug Logging", false, "Spammy logs for troubleshooting.");
			DebugLogArtifactList = config.Bind<bool>("Debug", "Log All Artifacts On Startup", false, "Logs artifact names on startup.");
			DebugLogCatalogRebuild = config.Bind<bool>("Debug", "Log Catalog Rebuild Details", false, "Logs when item rules are rebuilt from the catalog.");
			if (LiveReloadLists.Value)
			{
				BlacklistedItemsRaw.SettingChanged += delegate
				{
					TryRebuildFromCatalog("BlacklistedItems changed");
				};
				WhitelistItemsRaw.SettingChanged += delegate
				{
					TryRebuildFromCatalog("WhitelistItems changed");
				};
				CategoryBlacklistRaw.SettingChanged += delegate
				{
					TryRebuildFromCatalog("CategoryBlacklist changed");
				};
				PerItemScalingOverridesRaw.SettingChanged += delegate
				{
					TryRebuildFromCatalog("ItemOverrides changed");
				};
				PerItemStackOverridesRaw.SettingChanged += delegate
				{
					TryRebuildFromCatalog("StackOverrides changed");
				};
				UnsafeItemsRaw.SettingChanged += delegate
				{
					TryRebuildFromCatalog("UnsafeItems changed");
				};
				UnsafeUseManualList.SettingChanged += delegate
				{
					TryRebuildFromCatalog("Additional manual risky list toggle changed");
				};
				UnsafeAutomaticLegacyPatterns.SettingChanged += delegate
				{
					TryRebuildFromCatalog("Legacy unsafe patterns changed");
				};
				UseVanillaUnsafeCatalog.SettingChanged += delegate
				{
					TryRebuildFromCatalog("Vanilla unsafe catalog toggle changed");
				};
				RiskCatalogSensitivity.SettingChanged += delegate
				{
					TryRebuildFromCatalog("Unsafe catalog sensitivity changed");
				};
				WhitelistMode.SettingChanged += delegate
				{
					TryRebuildFromCatalog("WhitelistMode changed");
				};
			}
		}

		public void ParseBlacklist()
		{
			_blacklistedIndices.Clear();
			ParseNameList(BlacklistedItemsRaw.Value, _blacklistedIndices, "blacklist");
		}

		public void ParseWhitelist()
		{
			_whitelistedIndices.Clear();
			ParseNameList(WhitelistItemsRaw.Value, _whitelistedIndices, "whitelist");
		}

		public void ParseUnsafeItems()
		{
			_manualExtraUnsafeIndices.Clear();
			if (UnsafeUseManualList.Value)
			{
				ParseNameList(UnsafeItemsRaw.Value, _manualExtraUnsafeIndices, "manual-risky");
			}
		}

		public void ParseCategoryBlacklist()
		{
			_categoryBlacklist.Clear();
			foreach (string item in SplitListTokens(CategoryBlacklistRaw.Value))
			{
				if (Enum.TryParse<ItemCategory>(item, ignoreCase: true, out var result))
				{
					_categoryBlacklist.Add(result);
				}
				else
				{
					LogWarningOncePerRebuild("[category-blacklist] Unknown category token: '" + item + "'");
				}
			}
		}

		private void ParseNameList(string raw, HashSet<int> target, string labelForLog)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected I4, but got Unknown
			if (string.IsNullOrWhiteSpace(raw))
			{
				return;
			}
			foreach (string item in SplitListTokens(raw))
			{
				if (!ItemCatalogResolution.TryResolveItemToken(item, out var itemDef))
				{
					LogWarningOncePerRebuild("[" + labelForLog + "] Unknown or unloaded item token: '" + item + "'");
				}
				else
				{
					target.Add((int)itemDef.itemIndex);
					if (DebugLogCatalogRebuild.Value)
					{
						_log.LogInfo((object)("[" + labelForLog + "] Registered " + DescribeItem(itemDef)));
					}
				}
			}
		}

		public void ParseStackOverrides()
		{
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected I4, but got Unknown
			_stackCapByItemIndex.Clear();
			string value = PerItemStackOverridesRaw.Value;
			if (string.IsNullOrWhiteSpace(value))
			{
				return;
			}
			foreach (string item in SplitListTokens(value))
			{
				int num = item.IndexOf('=');
				if (num <= 0 || num >= item.Length - 1)
				{
					LogWarningOncePerRebuild("[Overrides] Invalid entry (expected Name=Number): '" + item + "'");
					continue;
				}
				string text = item.Substring(0, num).Trim();
				string text2 = item.Substring(num + 1).Trim();
				if (!int.TryParse(text2, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
				{
					LogWarningOncePerRebuild("[Overrides] Invalid number for '" + text + "': '" + text2 + "'");
					continue;
				}
				result = ClampPositiveStackCap(result);
				if (!ItemCatalogResolution.TryResolveItemToken(text, out var itemDef))
				{
					LogWarningOncePerRebuild("[Overrides] Unknown or unloaded item: '" + text + "'");
					continue;
				}
				_stackCapByItemIndex[(int)itemDef.itemIndex] = result;
				if (DebugLogCatalogRebuild.Value)
				{
					_log.LogInfo((object)$"[Overrides] {DescribeItem(itemDef)} effective max stack = {result}");
				}
			}
		}

		public void ParsePerItemScalingOverrides()
		{
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Expected I4, but got Unknown
			_scalingOverridesByItem.Clear();
			if (!EnablePerItemOverrides.Value)
			{
				return;
			}
			Dictionary<string, ItemScalingOverride> dictionary = new Dictionary<string, ItemScalingOverride>(StringComparer.OrdinalIgnoreCase);
			foreach (string item in SplitListTokens(PerItemScalingOverridesRaw.Value))
			{
				int num = item.IndexOf('=');
				int num2 = item.IndexOf('.');
				if (num <= 0 || num2 <= 0 || num2 >= num)
				{
					LogWarningOncePerRebuild("[ItemOverrides] Invalid entry: '" + item + "'");
					continue;
				}
				string text = item.Substring(0, num2).Trim();
				string text2 = item.Substring(num2 + 1, num - num2 - 1).Trim();
				string value = item.Substring(num + 1).Trim();
				if (text.Length != 0 && text2.Length != 0)
				{
					if (!dictionary.TryGetValue(text, out var value2))
					{
						value2 = (dictionary[text] = new ItemScalingOverride
						{
							Mode = DefaultScalingMode.Value,
							Growth = Math.Max(0.0001, GlobalGrowth.Value),
							Cap = Math.Max(1.0, SoftCapTarget.Value)
						});
					}
					ApplyOverrideField(value2, text, text2, value);
				}
			}
			foreach (KeyValuePair<string, ItemScalingOverride> item2 in dictionary)
			{
				if (!ItemCatalogResolution.TryResolveItemToken(item2.Key, out var itemDef))
				{
					LogWarningOncePerRebuild("[ItemOverrides] Unknown or unloaded item token: '" + item2.Key + "'");
				}
				else
				{
					_scalingOverridesByItem[(int)itemDef.itemIndex] = item2.Value;
				}
			}
		}

		private void ApplyOverrideField(ItemScalingOverride rule, string itemToken, string field, string value)
		{
			switch (field.ToLowerInvariant())
			{
			case "mode":
			{
				if (Enum.TryParse<ScalingMode>(value, ignoreCase: true, out var result3))
				{
					rule.Mode = result3;
					break;
				}
				LogWarningOncePerRebuild("[ItemOverrides] Invalid mode for '" + itemToken + "': '" + value + "'");
				break;
			}
			case "growth":
			{
				if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
				{
					rule.Growth = Math.Max(0.0001, result2);
					break;
				}
				LogWarningOncePerRebuild("[ItemOverrides] Invalid growth for '" + itemToken + "': '" + value + "'");
				break;
			}
			case "cap":
			{
				if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					rule.Cap = Math.Max(1.0, result);
					break;
				}
				LogWarningOncePerRebuild("[ItemOverrides] Invalid cap for '" + itemToken + "': '" + value + "'");
				break;
			}
			default:
				LogWarningOncePerRebuild("[ItemOverrides] Unknown field for '" + itemToken + "': '" + field + "'");
				break;
			}
		}

		private static IEnumerable<string> SplitListTokens(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				yield break;
			}
			char[] separator = new char[4] { ';', ',', '\n', '\r' };
			string[] array = raw.Split(separator, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					yield return text;
				}
			}
		}

		private static int ClampPositiveStackCap(int value)
		{
			if (value < 1)
			{
				return 1;
			}
			if (value > 536870911)
			{
				return 536870911;
			}
			return value;
		}

		public bool TryGetItemOverride(ItemDef def, out int maxStackCap)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected I4, but got Unknown
			maxStackCap = GetGlobalMaxStackClamped();
			if ((Object)(object)def == (Object)null)
			{
				return false;
			}
			if (_stackCapByItemIndex.TryGetValue((int)def.itemIndex, out var value))
			{
				maxStackCap = value;
				return true;
			}
			return false;
		}

		public int GetGlobalMaxStackClamped()
		{
			return ClampPositiveStackCap(MaxStack.Value);
		}

		public int GetEffectiveUnsafeHardCap()
		{
			return ClampPositiveStackCap(UnsafeHardCap.Value);
		}

		public int GetEffectiveMaxStack(ItemDef def)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected I4, but got Unknown
			if ((Object)(object)def == (Object)null)
			{
				return GetGlobalMaxStackClamped();
			}
			if (!_stackCapByItemIndex.TryGetValue((int)def.itemIndex, out var value))
			{
				return GetGlobalMaxStackClamped();
			}
			return value;
		}

		public bool TryGetScalingOverride(ItemDef def, out ItemScalingOverride itemOverride)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected I4, but got Unknown
			itemOverride = null;
			if ((Object)(object)def != (Object)null)
			{
				return _scalingOverridesByItem.TryGetValue((int)def.itemIndex, out itemOverride);
			}
			return false;
		}

		public bool IsManualExtraUnsafeItem(ItemDef def)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected I4, but got Unknown
			if ((Object)(object)def != (Object)null)
			{
				return _manualExtraUnsafeIndices.Contains((int)def.itemIndex);
			}
			return false;
		}

		public bool IsCategoryBlacklisted(ItemCategory category)
		{
			return _categoryBlacklist.Contains(category);
		}

		public bool IsItemExponentialBlocked(ItemDef itemDef)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected I4, but got Unknown
			if ((Object)(object)itemDef == (Object)null)
			{
				return true;
			}
			int item = (int)itemDef.itemIndex;
			if (WhitelistMode.Value)
			{
				if (_whitelistedIndices.Count != 0)
				{
					return !_whitelistedIndices.Contains(item);
				}
				return true;
			}
			return _blacklistedIndices.Contains(item);
		}

		public void RebuildFromCatalog(string reason)
		{
			try
			{
				_warnedTokens.Clear();
				ParseBlacklist();
				ParseWhitelist();
				ParseStackOverrides();
				ParseUnsafeItems();
				ParseCategoryBlacklist();
				ParsePerItemScalingOverrides();
				if (DebugLogging.Value || DebugLogCatalogRebuild.Value)
				{
					_log.LogInfo((object)$"Item catalog rules rebuilt ({reason}). Blacklist: {_blacklistedIndices.Count}, Whitelist: {_whitelistedIndices.Count}, Overrides: {_stackCapByItemIndex.Count}, Manual risky extras: {_manualExtraUnsafeIndices.Count}, ItemRules: {_scalingOverridesByItem.Count}");
				}
			}
			catch (Exception arg)
			{
				_log.LogError((object)$"Failed to rebuild item rules: {arg}");
			}
		}

		public void TryRebuildFromCatalog(string reason)
		{
			try
			{
				if (ItemCatalog.itemCount <= 0)
				{
					return;
				}
			}
			catch
			{
				return;
			}
			RebuildFromCatalog(reason);
		}

		private void LogWarningOncePerRebuild(string message)
		{
			if (_warnedTokens.Add(message))
			{
				_log.LogWarning((object)message);
			}
		}

		public double GetCategoryMultiplier(ItemCategory category)
		{
			return category switch
			{
				ItemCategory.Movement => Math.Max(0.0, MovementMultiplier.Value), 
				ItemCategory.Defense => Math.Max(0.0, DefenseMultiplier.Value), 
				ItemCategory.Proc => Math.Max(0.0, ProcMultiplier.Value), 
				ItemCategory.Healing => Math.Max(0.0, HealingMultiplier.Value), 
				ItemCategory.Utility => Math.Max(0.0, UtilityMultiplier.Value), 
				ItemCategory.Cooldown => Math.Max(0.0, CooldownMultiplier.Value), 
				_ => Math.Max(0.0, DamageMultiplier.Value), 
			};
		}

		private static string DescribeItem(ItemDef def)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected I4, but got Unknown
			if ((Object)(object)def == (Object)null)
			{
				return "(null)";
			}
			return string.Format("{0} (index={1})", ((Object)def).name ?? "?", (int)def.itemIndex);
		}
	}
	internal static class ExponentialStackMath
	{
		public static int GetNextTargetStack(int current, int applicableMaxStack, int exponentialBase)
		{
			applicableMaxStack = SanitizeMax(applicableMaxStack);
			if (current < 0)
			{
				current = 0;
			}
			if (current >= applicableMaxStack)
			{
				return current;
			}
			int num = Math.Max(2, exponentialBase);
			if (current == 0)
			{
				return Math.Min(1, applicableMaxStack);
			}
			double d = Math.Log(current, num);
			if (double.IsNaN(d) || double.IsInfinity(d))
			{
				int num2 = current + 1;
				if (num2 < applicableMaxStack)
				{
					return num2;
				}
				return applicableMaxStack;
			}
			double y = Math.Floor(d) + 1.0;
			double num3 = Math.Pow(num, y);
			int val = ((!double.IsNaN(num3) && !double.IsInfinity(num3) && !(num3 >= 2147483647.0)) ? ((int)num3) : 536870911);
			return Math.Min(applicableMaxStack, Math.Max(current + 1, val));
		}

		private static int SanitizeMax(int applicableMaxStack)
		{
			if (applicableMaxStack < 1)
			{
				return 1;
			}
			if (applicableMaxStack > 536870911)
			{
				return 536870911;
			}
			return applicableMaxStack;
		}
	}
	public static class ItemCatalogResolution
	{
		private static readonly Dictionary<string, string> TokenAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "PaulsGoatHoof", "Hoof" },
			{ "GoatHoof", "Hoof" },
			{ "HopooFeather", "Feather" },
			{ "TougherTimes", "Bear" },
			{ "SoldiersSyringe", "Syringe" },
			{ "SoldierSyringe", "Syringe" },
			{ "LensMakersGlasses", "CritGlasses" }
		};

		public static bool TryResolveItemToken(string token, out ItemDef itemDef)
		{
			itemDef = null;
			if (string.IsNullOrWhiteSpace(token))
			{
				return false;
			}
			token = token.Trim();
			if (token.Length == 0)
			{
				return false;
			}
			if (TokenAliases.TryGetValue(token, out var value))
			{
				token = value;
			}
			try
			{
				int itemCount = ItemCatalog.itemCount;
				for (int i = 0; i < itemCount; i++)
				{
					ItemDef itemDef2 = ItemCatalog.GetItemDef((ItemIndex)i);
					if (!((Object)(object)itemDef2 == (Object)null) && NameMatches(itemDef2, token))
					{
						itemDef = itemDef2;
						return true;
					}
				}
			}
			catch
			{
				itemDef = null;
				return false;
			}
			return false;
		}

		private static bool NameMatches(ItemDef def, string token)
		{
			if (string.Equals(((Object)def).name, token, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return false;
		}
	}
	internal readonly struct ScalingPlan
	{
		public readonly ScalingMode Mode;

		public readonly ItemCategory Category;

		public readonly int CurrentStack;

		public readonly int TargetStack;

		public readonly int EffectiveGain;

		public readonly double EffectiveMultiplier;

		public ScalingPlan(ScalingMode mode, ItemCategory category, int currentStack, int targetStack)
		{
			Mode = mode;
			Category = category;
			CurrentStack = currentStack;
			TargetStack = targetStack;
			EffectiveGain = Math.Max(0, targetStack - currentStack);
			EffectiveMultiplier = ((currentStack <= 0) ? ((double)targetStack) : ((double)targetStack / (double)currentStack));
		}
	}
	internal sealed class ItemScalingManager
	{
		private readonly ExponentialItemsConfig _config;

		private readonly ScalingFormulaRegistry _formulaRegistry;

		private readonly CategoryRegistry _categoryRegistry;

		private readonly UnsafeItemRegistry _unsafeRegistry;

		private readonly ProcSafetyManager _procSafety;

		public ItemScalingManager(ExponentialItemsConfig config, ScalingFormulaRegistry formulaRegistry, CategoryRegistry categoryRegistry, UnsafeItemRegistry unsafeRegistry, ProcSafetyManager procSafety)
		{
			_config = config;
			_formulaRegistry = formulaRegistry;
			_categoryRegistry = categoryRegistry;
			_unsafeRegistry = unsafeRegistry;
			_procSafety = procSafety;
		}

		public bool ShouldScaleItem(ItemDef itemDef)
		{
			if ((Object)(object)itemDef == (Object)null)
			{
				return false;
			}
			if (!IsAllowedByTier(itemDef))
			{
				return false;
			}
			if (_config.IsItemExponentialBlocked(itemDef))
			{
				return false;
			}
			ItemCategory category = _categoryRegistry.Resolve(itemDef);
			return !_config.IsCategoryBlacklisted(category);
		}

		public ScalingPlan BuildPlan(ItemDef itemDef, int currentStack)
		{
			ItemCategory category = _categoryRegistry.Resolve(itemDef);
			int effectiveMaxStack = _config.GetEffectiveMaxStack(itemDef);
			ScalingMode mode = ResolveMode(itemDef);
			double growth = ResolveGrowth(itemDef, category);
			double softCapTarget = ResolveCap(itemDef);
			ScalingRequest request = new ScalingRequest(currentStack, effectiveMaxStack, _config.BaseSize.Value, growth, softCapTarget);
			int nextStack = _formulaRegistry.GetNextStack(mode, request);
			nextStack = _procSafety.ClampResultingStack(nextStack);
			if (_config.DeterministicRounding.Value)
			{
				nextStack = Math.Max(currentStack + 1, nextStack);
			}
			return new ScalingPlan(mode, category, currentStack, nextStack);
		}

		public int ResolveGrantedAmount(ItemDef itemDef, int currentStack)
		{
			int requested = Math.Max(1, BuildPlan(itemDef, currentStack).EffectiveGain);
			return _procSafety.ClampGrantedAmount(requested);
		}

		private ScalingMode ResolveMode(ItemDef itemDef)
		{
			if (_config.TryGetScalingOverride(itemDef, out var itemOverride))
			{
				return itemOverride.Mode;
			}
			if (_unsafeRegistry.IsUnsafe(itemDef))
			{
				if (_config.UnsafeRespectCatalogScalingModes.Value && _unsafeRegistry.TryGetBuiltinProfile(itemDef, out var profile) && profile.Tier != 0)
				{
					return profile.RecommendedScaling;
				}
				return _config.UnsafeDefaultMode.Value;
			}
			return _config.DefaultScalingMode.Value;
		}

		private double ResolveGrowth(ItemDef itemDef, ItemCategory category)
		{
			double num = Math.Max(0.0001, _config.GlobalGrowth.Value);
			double categoryMultiplier = _config.GetCategoryMultiplier(category);
			double val = num * Math.Pow(y: Math.Max(0.0001, _config.CategoryExponent.Value), x: Math.Max(0.0001, categoryMultiplier));
			if (_config.TryGetScalingOverride(itemDef, out var itemOverride))
			{
				val = itemOverride.Growth;
			}
			return Math.Max(0.0001, val);
		}

		private double ResolveCap(ItemDef itemDef)
		{
			double result = Math.Max(1.0, _config.SoftCapTarget.Value);
			if (_config.TryGetScalingOverride(itemDef, out var itemOverride))
			{
				result = itemOverride.Cap;
			}
			else if (_unsafeRegistry.IsUnsafe(itemDef) && _config.UnsafeUseDiminishingReturns.Value)
			{
				result = Math.Max(1.0, _config.UnsafeSoftCapTarget.Value);
			}
			return result;
		}

		private bool IsAllowedByTier(ItemDef itemDef)
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected I4, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			ItemTier tier = itemDef.tier;
			switch ((int)tier)
			{
			default:
				if ((int)tier == 1000)
				{
					return _config.AffectTemporaryItems.Value;
				}
				return true;
			case 0:
				return _config.AffectTier1.Value;
			case 1:
				return _config.AffectTier2.Value;
			case 2:
				return _config.AffectTier3.Value;
			case 4:
				return _config.AffectBoss.Value;
			case 3:
				return _config.AffectLunar.Value;
			case 6:
			case 7:
			case 8:
			case 9:
				return _config.AffectVoid.Value;
			case 5:
				return _config.AffectTierless.Value;
			}
		}
	}
	internal static class PlayerDocs
	{
		public const string RequireArtifact = "When ON, this mod only changes how items stack while the Artifact of Exponents is enabled for the run. Turn OFF if you want faster stacking without enabling that artifact.";

		public const string MaxStack = "Absolute ceiling on stack count after this mod's math runs (unless a per-item cap in \"Per-Item Stack Hard Caps\" is lower). Maximum ~536,870,911 (int.MaxValue / 4) for math safety.";

		public const string BaseSize = "Exponent base used by Exponential mode only (minimum 2). Higher = bigger jumps between milestones.";

		public const string DefaultScalingMode = "Default growth curve for items that are not \"risky\" and do not have a per-item override.\n\nVanilla — Same as Linear (+1 per pickup).\n\nLinear — +1 stack per qualifying pickup.\n\nExponential — Jumps toward milestones (uses Exponential Base Size).\n\nLogarithmic — Gains based on log curve × Global Growth (usually gentler late run).\n\nSoftCap — Fast early growth that eases toward a plateau (uses Soft Cap Plateau Target).\n\nHyperbolic — Asymptotic curve; good for toned-down scaling.\n\nTip: With Deterministic +1 Pickup Rounding ON, every valid pickup still moves stacks forward by at least +1.";

		public const string GlobalGrowth = "Multiplier baked into formulas (especially Logarithmic and category-adjusted growth). Typical ~1.0–1.4.";

		public const string SoftCapTarget = "Default \"plateau width\" for SoftCap and Hyperbolic when an item does not set its own cap in overrides. Higher = higher stacks before diminishing kicks in hard.";

		public const string ActivePreset = "Startup preset that adjusts several numbers when you launch (you can still tweak after).\n\nCustom — No preset changes.\n\nVanillaPlus — Linear-ish, mild growth.\n\nChaos — Exponential with high growth.\n\nProcHell — Exponential with higher proc strength (still has proc depth guard).\n\nMMOScaling — Logarithmic-leaning with a roomy soft cap.\n\nHardcore — Hyperbolic with a tighter soft cap.\n\nMovementDisabled — Sets Movement Item Strength to 0 so movement buckets stop influencing growth.";

		public const string EnablePerItemOverrides = "When ON, the \"Per-Item Scaling Rules\" string is parsed. Turn OFF to ignore those rules without deleting them.";

		public const string WhitelistMode = "When ON, ONLY items listed under Whitelist Item Codes get accelerated stacking; everything else stays vanilla +1 for this mod.";

		public const string BlacklistedItems = "Internal item code names (comma or semicolon), NOT display titles. Those items never use this mod's accelerated stacking (always +1). Leave empty if you only want to tame items via the Risk Catalog instead.";

		public const string WhitelistItems = "Same format as blacklist. Only used when Whitelist Mode is ON.";

		public const string CategoryBlacklist = "Skip entire categories from accelerated stacking. Tokens (case-insensitive): Damage, Defense, Healing, Utility, Proc, Movement, Cooldown.\n\nExample: Movement,Proc";

		public const string PerItemScalingOverrides = "Advanced: ItemCode.Mode=SoftCap; ItemCode.Growth=1.1; ItemCode.Cap=120 — uses internal names (e.g. LensMakersGlasses).";

		public const string PerItemStackOverrides = "Advanced: ItemCode=256; OtherItem=64 — hard caps per item after scaling math.";

		public const string MovementMultiplier = "How strongly Movement-tagged items influence growth. Lower = less runaway speed scaling from the category system; 0 matches Movement Disabled preset for this bucket.";

		public const string DefenseMultiplier = "Strength multiplier for Defense-like items.";

		public const string ProcMultiplier = "Strength for proc/on-hit items. Raising this can worsen lag in extreme builds.";

		public const string HealingMultiplier = "Strength for healing-related items.";

		public const string UtilityMultiplier = "Strength for broad utility items.";

		public const string CooldownMultiplier = "Strength for cooldown-reduction style items.";

		public const string DamageMultiplier = "Strength for damage-focused items.";

		public const string CategoryExponent = "Exponent on category multipliers blended with Global Growth — higher = category dials matter more.";

		public const string UseVanillaUnsafeCatalog = "When ON (recommended), a built-in table classifies vanilla + DLC items by gameplay/systems risk for exponential stacking. Risky items use \"Fallback Risky Scaling Mode\" and optional tighter soft caps unless you override them per item.";

		public const string UnsafeCatalogSensitivity = "How many catalog tiers count as \"risky\" for scaling:\n\nExtremeOnly — Only the worst offenders (Gesture, Clover, Lysate, etc.).\n\nUnsafeAndAbove — Default. All Unsafe + Extreme catalog entries.\n\nMildAndAbove — Also includes Mild (crit glasses, many heal/regen items). More items use the risky curve.";

		public const string UnsafeRespectCatalogScalingModes = "When ON, risky items use each catalog entry's recommended curve (SoftCap, Hyperbolic, HardCap) instead of always using Fallback Risky Scaling Mode. Advanced — can feel very different from the global default.";

		public const string UnsafeItems = "Optional extra internal names to ALWAYS treat as risky (merged on top of the catalog). Only read when \"Use Additional Manual Risk List\" is ON.";

		public const string UnsafeUseManualList = "When ON, parses \"Additional Risky Item Codes\" and merges those items into the risky list. When OFF, that text is ignored.";

		public const string UnsafeAutomaticLegacyPatterns = "DANGEROUS for mod compatibility: substring match on internal names (Hoof, Feather, Quail, Syringe, Times). Prefer the built-in catalog for vanilla. Turn ON only if you need fuzzy matching for custom items.";

		public const string UnsafeDefaultMode = "Scaling mode for risky items when \"Use Catalog Recommended Scaling Modes\" is OFF (or the item has no catalog mode). Default SoftCap is vanilla-friendly.";

		public const string UnsafeUseDiminishingReturns = "When ON, risky items use the tighter \"Risky Items Soft Cap Target\" for plateau width unless a per-item override sets Cap.";

		public const string UnsafeSoftCapTarget = "Plateau width for risky items when Tighter Soft Cap is ON — usually lower than the global Soft Cap Plateau Target.";

		public const string UnsafeHardCap = "Safety valve: maximum stacks after scaling math (applies to all items through the proc clamp path). Same absolute ceiling as Maximum Stack Count (~536,870,911).";

		public const string MaxProcDepth = "Maximum proc chain depth the mod uses when clamping huge single-frame grants. Lower = safer CPU, stricter anti-spam.";

		public const string MaxProjectilesPerFrame = "Caps how many stacks can be granted in one frame from a single accelerated pickup burst — prevents single-frame spikes.";

		public const string MaxVelocity = "Reference guardrail value paired with movement/proc tuning in config (full combat clamping depends on other mods/hooks).";

		public const string MaxAttackSpeed = "Reference attack speed ceiling used in the mod's safety tuning metadata.";

		public const string MaxDamageCoefficient = "Very high damage coefficient ceiling used as a numerical guardrail in scaling safety.";

		public const string EnableTooltipInfo = "When ON, item descriptions can show short lines about scaling mode and estimated gains (informational).";

		public const string TooltipShowRiskAudit = "When ON (and tooltips are ON), also shows catalog risk tier, behavior flags, and suggested hard cap hints for audited items. For advanced users.";

		public const string LiveReloadLists = "When ON, editing the cfg file can refresh blacklist / overrides without restarting (when the game is in a good state).";

		public const string DeterministicRounding = "When ON, each qualifying pickup still moves stacks by at least +1 toward the computed target.";

		public const string DebugLogging = "Spammy logs for troubleshooting.";

		public const string DebugLogArtifactList = "Logs artifact names on startup.";

		public const string DebugLogCatalogRebuild = "Logs when item rules are rebuilt from the catalog.";

		public const string AffectTier1 = "Allow white (common) items to use accelerated stacking.";

		public const string AffectTier2 = "Allow green (uncommon) items.";

		public const string AffectTier3 = "Allow red (legendary) items.";

		public const string AffectBoss = "Allow boss (yellow) items.";

		public const string AffectLunar = "Allow lunar items.";

		public const string AffectVoid = "Allow void-tier items.";

		public const string AffectTierless = "Allow hidden/no-tier items (risky; off by default).";

		public const string AffectTemporaryItems = "Allow runtime temporary items (usually off).";
	}
	internal sealed class ProcSafetyManager
	{
		private readonly ExponentialItemsConfig _config;

		public ProcSafetyManager(ExponentialItemsConfig config)
		{
			_config = config ?? throw new ArgumentNullException("config");
		}

		public int ClampGrantedAmount(int requested)
		{
			if (requested < 1)
			{
				return 1;
			}
			return Math.Min(requested, _config.MaxProjectilesPerFrame.Value);
		}

		public int ClampResultingStack(int stack)
		{
			return Math.Min(stack, _config.GetEffectiveUnsafeHardCap());
		}
	}
	internal static class RiskOfOptionsIntegration
	{
		private static bool _registered;

		private static MethodInfo method_AddOption;

		private static MethodInfo method_SetModDescription;

		public static void TryRegister(ManualLogSource log, ExponentialItemsConfig c)
		{
			if (_registered)
			{
				return;
			}
			Assembly assembly = FindRiskOfOptionsAssembly();
			if (assembly == null)
			{
				return;
			}
			try
			{
				Type type = assembly.GetType("RiskOfOptions.ModSettingsManager");
				if (type == null)
				{
					log.LogWarning((object)"[EvenBetterExponentialItems] RiskOfOptions.ModSettingsManager not found.");
					return;
				}
				MethodInfo[] source = (from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public)
					where m.Name == "AddOption" && m.GetParameters().Length == 1
					select m).ToArray();
				method_AddOption = source.FirstOrDefault((MethodInfo m) => m.GetParameters()[0].ParameterType.FullName == "RiskOfOptions.Options.BaseOption") ?? source.FirstOrDefault();
				if (method_AddOption == null)
				{
					log.LogWarning((object)"[EvenBetterExponentialItems] RiskOfOptions AddOption API mismatch.");
					return;
				}
				method_SetModDescription = type.GetMethod("SetModDescription", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				method_SetModDescription?.Invoke(null, new object[1] { "Even Better Exponential Items — faster stacking with guardrails. Start with presets under General Scaling, then open Risky Items if you want to tune the built-in safety catalog. Every option has a tooltip; the same text is in your BepInEx config file under clearer section names." });
				AddCheckbox(assembly, c.RequireArtifactEnabled);
				AddIntSlider(assembly, c.MaxStack, 1, 536870911);
				AddIntSlider(assembly, c.BaseSize, 2, 10);
				AddEnumChoice<ScalingMode>(assembly, c.DefaultScalingMode);
				AddEnumChoice<ScalingPreset>(assembly, c.ActivePreset);
				AddCheckbox(assembly, c.EnablePerItemOverrides);
				TryAddStepSliderDouble(assembly, c.GlobalGrowth, 0.05, 5.0, 0.01);
				TryAddStepSliderDouble(assembly, c.SoftCapTarget, 10.0, 2000.0, 5.0);
				AddCheckbox(assembly, c.AffectTier1);
				AddCheckbox(assembly, c.AffectTier2);
				AddCheckbox(assembly, c.AffectTier3);
				AddCheckbox(assembly, c.AffectBoss);
				AddCheckbox(assembly, c.AffectLunar);
				AddCheckbox(assembly, c.AffectVoid);
				AddCheckbox(assembly, c.AffectTierless);
				AddCheckbox(assembly, c.AffectTemporaryItems);
				AddCheckbox(assembly, c.WhitelistMode);
				AddStringField(assembly, c.BlacklistedItemsRaw);
				AddStringField(assembly, c.WhitelistItemsRaw);
				AddStringField(assembly, c.CategoryBlacklistRaw);
				AddStringField(assembly, c.PerItemScalingOverridesRaw);
				AddStringField(assembly, c.PerItemStackOverridesRaw);
				TryAddStepSliderDouble(assembly, c.MovementMultiplier, 0.0, 3.0, 0.05);
				TryAddStepSliderDouble(assembly, c.DefenseMultiplier, 0.0, 3.0, 0.05);
				TryAddStepSliderDouble(assembly, c.ProcMultiplier, 0.0, 3.0, 0.05);
				TryAddStepSliderDouble(assembly, c.HealingMultiplier, 0.0, 3.0, 0.05);
				TryAddStepSliderDouble(assembly, c.UtilityMultiplier, 0.0, 3.0, 0.05);
				TryAddStepSliderDouble(assembly, c.CooldownMultiplier, 0.0, 3.0, 0.05);
				TryAddStepSliderDouble(assembly, c.DamageMultiplier, 0.0, 3.0, 0.05);
				TryAddStepSliderDouble(assembly, c.CategoryExponent, 0.1, 5.0, 0.05);
				AddCheckbox(assembly, c.UseVanillaUnsafeCatalog);
				AddEnumChoice<UnsafeCatalogSensitivity>(assembly, c.RiskCatalogSensitivity);
				AddCheckbox(assembly, c.UnsafeRespectCatalogScalingModes);
				AddCheckbox(assembly, c.UnsafeUseManualList);
				AddStringField(assembly, c.UnsafeItemsRaw);
				AddCheckbox(assembly, c.UnsafeAutomaticLegacyPatterns);
				AddEnumChoice<ScalingMode>(assembly, c.UnsafeDefaultMode);
				AddCheckbox(assembly, c.UnsafeUseDiminishingReturns);
				TryAddStepSliderDouble(assembly, c.UnsafeSoftCapTarget, 5.0, 500.0, 1.0);
				AddIntSlider(assembly, c.UnsafeHardCap, 1, 536870911);
				AddIntSlider(assembly, c.MaxProcDepth, 1, 64);
				AddIntSlider(assembly, c.MaxProjectilesPerFrame, 1, 500);
				TryAddSliderFloat(assembly, c.MaxVelocity, 50f, 2000f);
				TryAddSliderFloat(assembly, c.MaxAttackSpeed, 1f, 50f);
				TryAddStepSliderDouble(assembly, c.MaxDamageCoefficient, 1000.0, 5000000.0, 1000.0);
				AddCheckbox(assembly, c.EnableTooltipInfo);
				AddCheckbox(assembly, c.TooltipShowRiskAudit);
				AddCheckbox(assembly, c.LiveReloadLists);
				AddCheckbox(assembly, c.DeterministicRounding);
				AddCheckbox(assembly, c.DebugLogging);
				AddCheckbox(assembly, c.DebugLogArtifactList);
				AddCheckbox(assembly, c.DebugLogCatalogRebuild);
				_registered = true;
				log.LogInfo((object)"Risk of Options: in-game settings registered for Even Better Exponential Items.");
			}
			catch (Exception ex)
			{
				log.LogWarning((object)("Risk of Options integration failed — config file still works. " + ex.Message));
			}
		}

		private static Assembly FindRiskOfOptionsAssembly()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					if (string.Equals(assembly.GetName().Name, "RiskOfOptions", StringComparison.Ordinal))
					{
						return assembly;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static void AddCheckbox(Assembly roo, ConfigEntry<bool> entry)
		{
			Type type = roo.GetType("RiskOfOptions.Options.CheckBoxOption");
			if (!(type == null))
			{
				object obj = Activator.CreateInstance(type, entry);
				method_AddOption.Invoke(null, new object[1] { obj });
			}
		}

		private static void AddIntSlider(Assembly roo, ConfigEntry<int> entry, int min, int max)
		{
			Type type = roo.GetType("RiskOfOptions.OptionConfigs.IntSliderConfig");
			Type type2 = roo.GetType("RiskOfOptions.Options.IntSliderOption");
			if (!(type == null) && !(type2 == null))
			{
				object obj = Activator.CreateInstance(type);
				FieldInfo field = type.GetField("min");
				FieldInfo? field2 = type.GetField("max");
				field?.SetValue(obj, min);
				field2?.SetValue(obj, max);
				object obj2 = Activator.CreateInstance(type2, entry, obj);
				method_AddOption.Invoke(null, new object[1] { obj2 });
			}
		}

		private static void TryAddSliderFloat(Assembly roo, ConfigEntry<float> entry, float min, float max)
		{
			Type type = roo.GetType("RiskOfOptions.OptionConfigs.SliderConfig");
			Type type2 = roo.GetType("RiskOfOptions.Options.SliderOption");
			if (type == null || type2 == null)
			{
				return;
			}
			try
			{
				object obj = Activator.CreateInstance(type);
				type.GetField("min")?.SetValue(obj, min);
				type.GetField("max")?.SetValue(obj, max);
				object obj2 = Activator.CreateInstance(type2, entry, obj);
				method_AddOption.Invoke(null, new object[1] { obj2 });
			}
			catch
			{
			}
		}

		private static void TryAddStepSliderDouble(Assembly roo, ConfigEntry<double> entry, double min, double max, double step)
		{
			Type type = roo.GetType("RiskOfOptions.OptionConfigs.StepSliderConfig");
			Type type2 = roo.GetType("RiskOfOptions.Options.StepSliderOption");
			if (type == null || type2 == null)
			{
				return;
			}
			try
			{
				object obj = Activator.CreateInstance(type);
				type.GetField("min")?.SetValue(obj, (float)min);
				type.GetField("max")?.SetValue(obj, (float)max);
				type.GetField("increment")?.SetValue(obj, (float)step);
				object obj2 = Activator.CreateInstance(type2, entry, obj);
				method_AddOption.Invoke(null, new object[1] { obj2 });
			}
			catch
			{
			}
		}

		private static void AddStringField(Assembly roo, ConfigEntry<string> entry)
		{
			Type type = roo.GetType("RiskOfOptions.Options.StringInputFieldOption");
			if (!(type == null))
			{
				object obj = Activator.CreateInstance(type, entry);
				method_AddOption.Invoke(null, new object[1] { obj });
			}
		}

		private static void AddEnumChoice<T>(Assembly roo, ConfigEntry<T> entry) where T : struct, IConvertible
		{
			Type type = roo.GetType("RiskOfOptions.Options.ChoiceOption");
			if (type == null)
			{
				return;
			}
			try
			{
				object obj = Activator.CreateInstance(type, entry);
				method_AddOption.Invoke(null, new object[1] { obj });
			}
			catch
			{
			}
		}
	}
	internal readonly struct ScalingRequest
	{
		public readonly int Current;

		public readonly int MaxCap;

		public readonly int BaseSize;

		public readonly double Growth;

		public readonly double SoftCapTarget;

		public ScalingRequest(int current, int maxCap, int baseSize, double growth, double softCapTarget)
		{
			Current = Math.Max(0, current);
			MaxCap = Math.Max(1, maxCap);
			BaseSize = Math.Max(2, baseSize);
			Growth = Math.Max(0.0001, growth);
			SoftCapTarget = Math.Max(1.0, softCapTarget);
		}
	}
	internal sealed class ScalingFormulaRegistry
	{
		public int GetNextStack(ScalingMode mode, ScalingRequest request)
		{
			switch (mode)
			{
			case ScalingMode.Linear:
				return Clamp(request.Current + 1, request.MaxCap);
			case ScalingMode.Logarithmic:
				return Clamp(request.Current + (int)Math.Max(1.0, Math.Log((double)request.Current + 2.0, 2.0) * request.Growth), request.MaxCap);
			case ScalingMode.Hyperbolic:
			{
				double num2 = (double)request.Current + 1.0;
				return Clamp((int)Math.Ceiling(request.SoftCapTarget * num2 / (request.SoftCapTarget + num2)), request.MaxCap);
			}
			case ScalingMode.SoftCap:
			{
				double num = (double)request.Current + 1.0;
				return Clamp((int)Math.Ceiling(request.SoftCapTarget * (1.0 - Math.Exp((0.0 - num) / request.SoftCapTarget))), request.MaxCap);
			}
			case ScalingMode.Vanilla:
				return Clamp(request.Current + 1, request.MaxCap);
			default:
				return ExponentialStackMath.GetNextTargetStack(request.Current, request.MaxCap, request.BaseSize);
			}
		}

		private static int Clamp(int value, int cap)
		{
			if (value < 1)
			{
				return 1;
			}
			if (value > cap)
			{
				return cap;
			}
			return value;
		}
	}
	internal sealed class TooltipManager
	{
		private readonly ExponentialItemsConfig _config;

		private readonly ItemScalingManager _scaling;

		private readonly UnsafeItemRegistry _unsafeRegistry;

		public TooltipManager(ExponentialItemsConfig config, ItemScalingManager scaling, UnsafeItemRegistry unsafeRegistry)
		{
			_config = config;
			_scaling = scaling;
			_unsafeRegistry = unsafeRegistry;
		}

		public string BuildTooltipSuffix(ItemDef itemDef, int currentStack)
		{
			if (!_config.EnableTooltipInfo.Value || (Object)(object)itemDef == (Object)null)
			{
				return string.Empty;
			}
			ScalingPlan scalingPlan = _scaling.BuildPlan(itemDef, currentStack);
			double num = ((scalingPlan.TargetStack <= 0) ? 0.0 : (1.0 - (double)scalingPlan.EffectiveGain / (double)Mathf.Max(1, scalingPlan.TargetStack)));
			string text = $"\n<color=#88D5FF>Base: +1</color>\n<color=#D6B4FF>Scaled: +{scalingPlan.EffectiveGain}</color>\nMode: {scalingPlan.Mode}\nDiminishing: {num * 100.0:F1}%\nEffective Multiplier: {scalingPlan.EffectiveMultiplier:F2}x";
			if (_config.TooltipShowRiskAudit.Value && _unsafeRegistry.TryGetBuiltinProfile(itemDef, out var profile))
			{
				text += $"\n<color=#FFB454>Risk tier: {profile.Tier}</color>\nFlags: {FormatFlags(profile.Flags)}";
				if (profile.SuggestedHardStackCap.HasValue)
				{
					text += $"\nSuggested hard cap: {profile.SuggestedHardStackCap.Value}";
				}
			}
			return text;
		}

		private static string FormatFlags(UnsafeBehaviorFlags f)
		{
			if (f != 0)
			{
				return f.ToString();
			}
			return "None";
		}
	}
	[Flags]
	public enum UnsafeBehaviorFlags
	{
		None = 0,
		Movement = 1,
		ProcRecursion = 2,
		CooldownReduction = 4,
		DodgeOrInvulnerability = 8,
		VelocityScaling = 0x10,
		AttackSpeed = 0x20,
		ProjectileSpam = 0x40,
		AiBreaking = 0x80,
		PhysicsInstability = 0x100,
		DamageOverflow = 0x200,
		HealingRecursion = 0x400,
		InfiniteUptime = 0x800,
		MultiplayerDesyncRisk = 0x1000,
		ServerPerformanceRisk = 0x2000
	}
	public enum UnsafeRiskTier
	{
		Safe,
		Mild,
		Unsafe,
		Extreme
	}
	public enum UnsafeCatalogSensitivity
	{
		ExtremeOnly,
		UnsafeAndAbove,
		MildAndAbove
	}
	public readonly struct BuiltinUnsafeItemProfile
	{
		public UnsafeRiskTier Tier { get; }

		public UnsafeBehaviorFlags Flags { get; }

		public ScalingMode RecommendedScaling { get; }

		public int? SuggestedHardStackCap { get; }

		public string Guidance { get; }

		public BuiltinUnsafeItemProfile(UnsafeRiskTier tier, UnsafeBehaviorFlags flags, ScalingMode recommendedScaling, int? suggestedHardStackCap, string guidance)
		{
			Tier = tier;
			Flags = flags;
			RecommendedScaling = recommendedScaling;
			SuggestedHardStackCap = suggestedHardStackCap;
			Guidance = guidance ?? string.Empty;
		}
	}
	internal static class UnsafeCatalogTierOrder
	{
		internal static int MinimumTierToTrigger(UnsafeCatalogSensitivity sensitivity)
		{
			return sensitivity switch
			{
				UnsafeCatalogSensitivity.ExtremeOnly => 3, 
				UnsafeCatalogSensitivity.UnsafeAndAbove => 2, 
				UnsafeCatalogSensitivity.MildAndAbove => 1, 
				_ => 2, 
			};
		}

		internal static bool CatalogTierTriggersUnsafe(UnsafeRiskTier tier, UnsafeCatalogSensitivity sensitivity)
		{
			return (int)tier >= MinimumTierToTrigger(sensitivity);
		}
	}
	internal sealed class UnsafeItemRegistry
	{
		private readonly ExponentialItemsConfig _config;

		public UnsafeItemRegistry(ExponentialItemsConfig config)
		{
			_config = config;
		}

		public bool IsUnsafe(ItemDef itemDef)
		{
			if ((Object)(object)itemDef == (Object)null)
			{
				return false;
			}
			if (_config.IsManualExtraUnsafeItem(itemDef))
			{
				return true;
			}
			if (_config.UseVanillaUnsafeCatalog.Value && VanillaUnsafeItemCatalog.TryGetProfile(itemDef, out var profile) && UnsafeCatalogTierOrder.CatalogTierTriggersUnsafe(profile.Tier, _config.RiskCatalogSensitivity.Value))
			{
				return true;
			}
			if (!_config.UnsafeAutomaticLegacyPatterns.Value)
			{
				return false;
			}
			return MatchesLegacyNamePattern(((Object)itemDef).name);
		}

		public bool TryGetBuiltinProfile(ItemDef itemDef, out BuiltinUnsafeItemProfile profile)
		{
			return VanillaUnsafeItemCatalog.TryGetProfile(itemDef, out profile);
		}

		internal static bool MatchesLegacyNamePattern(string itemDefName)
		{
			if (string.IsNullOrEmpty(itemDefName))
			{
				return false;
			}
			if (itemDefName.IndexOf("Hoof", StringComparison.OrdinalIgnoreCase) >= 0 || itemDefName.IndexOf("Feather", StringComparison.OrdinalIgnoreCase) >= 0 || itemDefName.IndexOf("Quail", StringComparison.OrdinalIgnoreCase) >= 0 || itemDefName.IndexOf("Syringe", StringComparison.OrdinalIgnoreCase) >= 0 || itemDefName.IndexOf("Times", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			return false;
		}
	}
	internal static class VanillaUnsafeItemCatalog
	{
		private static readonly Dictionary<string, BuiltinUnsafeItemProfile> Profiles;

		public static int ProfileCount => Profiles.Count;

		static VanillaUnsafeItemCatalog()
		{
			Profiles = new Dictionary<string, BuiltinUnsafeItemProfile>(StringComparer.OrdinalIgnoreCase);
			Reg("AutoCastEquipment", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.InfiniteUptime | UnsafeBehaviorFlags.MultiplayerDesyncRisk | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 8, "Gesture of the Drowned: equipment + CDR feedback loops.");
			Reg("LunarBadLuck", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.DamageOverflow, ScalingMode.Hyperbolic, 15, "Purity: warps luck and proc coefficients.");
			Reg("EquipmentMagazineVoid", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.InfiniteUptime | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 12, "Lysate Cell: extra equipment charges.");
			Reg("RandomEquipmentTrigger", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.MultiplayerDesyncRisk | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 8, "Bottled Chaos.");
			Reg("Clover", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 25, "57 Leaf Clover: combinatorial proc luck.");
			Reg("CloverVoid", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.AiBreaking | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 8, "Benthic Bloom.");
			Reg("LunarSun", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.InfiniteUptime | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 12, "Egocentrism.");
			Reg("DroneWeapons", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.ProjectileSpam | UnsafeBehaviorFlags.AiBreaking | UnsafeBehaviorFlags.MultiplayerDesyncRisk | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 15, "Spare Drone Parts.");
			Reg("VoidMegaCrabItem", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.AiBreaking | UnsafeBehaviorFlags.MultiplayerDesyncRisk | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 6, "Newly Hatched Zoea.");
			Reg("PhysicsProjectile", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.ProjectileSpam | UnsafeBehaviorFlags.PhysicsInstability | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 5, "Orphaned Core.");
			Reg("GhostOnKill", UnsafeRiskTier.Extreme, UnsafeBehaviorFlags.AiBreaking | UnsafeBehaviorFlags.MultiplayerDesyncRisk | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 12, "Happiest Mask.");
			Reg("Hoof", UnsafeRiskTier.Unsafe, Mv(), ScalingMode.SoftCap, 120, "Paul's Goat Hoof.");
			Reg("Feather", UnsafeRiskTier.Unsafe, Mv(), ScalingMode.Hyperbolic, 40, "Hopoo Feather.");
			Reg("JumpBoost", UnsafeRiskTier.Unsafe, Mv(), ScalingMode.SoftCap, 80, "Wax Quail.");
			Reg("SprintBonus", UnsafeRiskTier.Unsafe, Mv(), ScalingMode.SoftCap, 100, "Energy Drink.");
			Reg("SprintArmor", UnsafeRiskTier.Unsafe, Mv(), ScalingMode.SoftCap, 80, "Rose Buckler.");
			Reg("SprintOutOfCombat", UnsafeRiskTier.Unsafe, Mv(), ScalingMode.SoftCap, 80, "Red Whip.");
			Reg("MoveSpeedOnKill", UnsafeRiskTier.Unsafe, Mv(), ScalingMode.SoftCap, 60, "Hunter's Harpoon.");
			Reg("Phasing", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.Movement | UnsafeBehaviorFlags.DodgeOrInvulnerability | UnsafeBehaviorFlags.VelocityScaling | UnsafeBehaviorFlags.MultiplayerDesyncRisk, ScalingMode.Hyperbolic, 25, "Old War Stealthkit.");
			Reg("FallBoots", UnsafeRiskTier.Unsafe, Mv() | UnsafeBehaviorFlags.ProjectileSpam | UnsafeBehaviorFlags.PhysicsInstability, ScalingMode.Hyperbolic, 15, "H3AD-5T v2.");
			Reg("Mocha", UnsafeRiskTier.Unsafe, Mv() | UnsafeBehaviorFlags.AttackSpeed, ScalingMode.SoftCap, 80, "Mocha.");
			Reg("BoostAllStats", UnsafeRiskTier.Unsafe, Mv() | UnsafeBehaviorFlags.AttackSpeed, ScalingMode.SoftCap, 60, "Growth Nectar.");
			Reg("SpeedBoostPickup", UnsafeRiskTier.Unsafe, Mv(), ScalingMode.SoftCap, 80, "Elusive Antlers.");
			Reg("SpeedOnPickup", UnsafeRiskTier.Unsafe, Mv(), ScalingMode.SoftCap, 80, "Collector's Compulsion.");
			Reg("MushroomVoid", UnsafeRiskTier.Unsafe, Mv() | UnsafeBehaviorFlags.HealingRecursion, ScalingMode.SoftCap, 60, "Weeping Fungus.");
			Reg("WarCryOnMultiKill", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.Movement | UnsafeBehaviorFlags.AttackSpeed, ScalingMode.SoftCap, 80, "Berzerker's Pauldron.");
			Reg("WardOnLevel", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.Movement | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 40, "Warbanner.");
			Reg("JumpDamageStrike", UnsafeRiskTier.Unsafe, Mv() | UnsafeBehaviorFlags.ProjectileSpam, ScalingMode.SoftCap, 40, "Faraday Spur.");
			Reg("CritAtLowerElevation", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.Movement | UnsafeBehaviorFlags.DamageOverflow, ScalingMode.SoftCap, 60, "Hiker's Boots.");
			Reg("TeleportOnLowHealth", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.Movement | UnsafeBehaviorFlags.MultiplayerDesyncRisk, ScalingMode.SoftCap, 30, "Unstable Transmitter.");
			Reg("KnockBackHitEnemies", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.PhysicsInstability | UnsafeBehaviorFlags.MultiplayerDesyncRisk, ScalingMode.SoftCap, 40, "Breaching Fin.");
			Reg("Syringe", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.AttackSpeed, ScalingMode.SoftCap, 100, "Soldier's Syringe.");
			Reg("AttackSpeedOnCrit", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.AttackSpeed | UnsafeBehaviorFlags.HealingRecursion, ScalingMode.SoftCap, 60, "Predatory Instincts.");
			Reg("AttackSpeedPerNearbyAllyOrEnemy", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.AttackSpeed | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 40, "Bolstering Lantern.");
			Reg("Bandolier", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.CooldownReduction, ScalingMode.SoftCap, 60, "Bandolier.");
			Reg("AlienHead", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 40, "Alien Head.");
			Reg("EquipmentMagazine", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.InfiniteUptime, ScalingMode.SoftCap, 25, "Fuel Cell.");
			Reg("Talisman", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.CooldownReduction, ScalingMode.SoftCap, 30, "Soulbound Catalyst.");
			Reg("KillEliteFrenzy", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.AttackSpeed | UnsafeBehaviorFlags.InfiniteUptime, ScalingMode.Hyperbolic, 25, "Brainstalks.");
			Reg("HalfAttackSpeedHalfCooldowns", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.AttackSpeed | UnsafeBehaviorFlags.DamageOverflow, ScalingMode.Hyperbolic, 20, "Light Flux Pauldron.");
			Reg("HalfSpeedDoubleHealth", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.Movement | UnsafeBehaviorFlags.DamageOverflow, ScalingMode.Hyperbolic, 15, "Stone Flux Pauldron.");
			Reg("EnergizedOnEquipmentUse", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.AttackSpeed, ScalingMode.SoftCap, 40, "War Horn.");
			Reg("UtilitySkillMagazine", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.CooldownReduction | UnsafeBehaviorFlags.ProjectileSpam, ScalingMode.SoftCap, 25, "Hardlight Afterburner.");
			Reg("Bear", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.DodgeOrInvulnerability | UnsafeBehaviorFlags.DamageOverflow, ScalingMode.Hyperbolic, 200, "Tougher Times.");
			Reg("BearVoid", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.DodgeOrInvulnerability | UnsafeBehaviorFlags.HealingRecursion, ScalingMode.Hyperbolic, 120, "Safer Spaces.");
			Reg("ChainLightning", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 60, "Ukulele.");
			Reg("ChainLightningVoid", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 50, "Polylute.");
			Reg("Missile", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 60, "AtG Missile Mk.1.");
			Reg("MissileVoid", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 50, "Plasma Shrimp.");
			Reg("MoreMissile", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 25, "Pocket I.C.B.M.");
			Reg("Dagger", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.AiBreaking, ScalingMode.SoftCap, 40, "Ceremonial Dagger.");
			Reg("Behemoth", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.PhysicsInstability, ScalingMode.SoftCap, 25, "Brilliant Behemoth.");
			Reg("FireballsOnHit", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 40, "Molten Perforator.");
			Reg("LightningStrikeOnHit", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 40, "Charged Perforator.");
			Reg("BounceNearby", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.PhysicsInstability, ScalingMode.SoftCap, 30, "Sentient Meat Hook.");
			Reg("StunAndPierce", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 40, "Electric Boomerang.");
			Reg("Icicle", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 35, "Frost Relic.");
			Reg("NovaOnHeal", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.HealingRecursion, ScalingMode.SoftCap, 35, "N'kuhana's Opinion.");
			Reg("ExplodeOnDeath", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 50, "Will-o'-the-wisp.");
			Reg("ExplodeOnDeathVoid", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 45, "Voidsent Flame.");
			Reg("IgniteOnKill", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 60, "Gasoline.");
			Reg("StrengthenBurn", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 50, "Ignition Tank.");
			Reg("StickyBomb", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 80, "Sticky Bomb.");
			Reg("ShockNearby", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 25, "Unstable Tesla Coil.");
			Reg("LaserTurbine", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 25, "Resonance Disc.");
			Reg("BarrageOnBoss", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 30, "War Bonds.");
			Reg("Firework", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 40, "Bundle of Fireworks.");
			Reg("Squid", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.AiBreaking, ScalingMode.SoftCap, 40, "Squid Polyp.");
			Reg("DronesDropDynamite", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.PhysicsInstability | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 15, "Box of Dynamite.");
			Reg("ElementalRingVoid", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 45, "Singularity Band.");
			Reg("PrimarySkillShuriken", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 45, "Shuriken.");
			Reg("BleedOnHit", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion, ScalingMode.SoftCap, 80, "Tri-Tip Dagger.");
			Reg("BleedOnHitVoid", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion, ScalingMode.SoftCap, 60, "Needletick.");
			Reg("DeathMark", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion, ScalingMode.SoftCap, 60, "Death Mark.");
			Reg("Thorns", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 60, "Razorwire.");
			Reg("StunChanceOnHit", UnsafeRiskTier.Unsafe, Pv(), ScalingMode.SoftCap, 80, "Stun Grenade.");
			Reg("SlowOnHit", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion, ScalingMode.SoftCap, 60, "Chronobauble.");
			Reg("SlowOnHitVoid", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion, ScalingMode.SoftCap, 50, "Tentabauble.");
			Reg("TriggerEnemyDebuffs", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 35, "Noxious Thorn.");
			Reg("NovaOnLowHealth", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.HealingRecursion, ScalingMode.SoftCap, 25, "Genesis Loop.");
			Reg("HeadHunter", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.AiBreaking, ScalingMode.SoftCap, 25, "Wake of Vultures.");
			Reg("MeteorAttackOnHighDamage", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 20, "Runic Lens.");
			Reg("ShockDamageAura", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 20, "Faulty Conductor (item).");
			Reg("SharedSuffering", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.MultiplayerDesyncRisk, ScalingMode.SoftCap, 25, "Networked Suffering.");
			Reg("BleedOnHitAndExplode", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.PhysicsInstability, ScalingMode.SoftCap, 25, "Shatterspleen.");
			Reg("SprintWisp", UnsafeRiskTier.Unsafe, Pv() | UnsafeBehaviorFlags.Movement, ScalingMode.SoftCap, 35, "Little Disciple.");
			Reg("WarCryOnCombat", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.Movement | UnsafeBehaviorFlags.AttackSpeed, ScalingMode.SoftCap, 50, "War Cry on combat.");
			Reg("DelayedDamage", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.MultiplayerDesyncRisk, ScalingMode.SoftCap, 30, "Warped Echo.");
			Reg("IncreasePrimaryDamage", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProjectileSpam | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 35, "Luminous Shot.");
			Reg("PermanentDebuffOnHit", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.DamageOverflow | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.Hyperbolic, 40, "Symbiotic Scorpion.");
			Reg("TransferDebuffOnHit", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.DamageOverflow | UnsafeBehaviorFlags.MultiplayerDesyncRisk, ScalingMode.Hyperbolic, 20, "Neutronium Weight.");
			Reg("LunarDagger", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.DamageOverflow | UnsafeBehaviorFlags.HealingRecursion, ScalingMode.Hyperbolic, 15, "Shaped Glass.");
			Reg("ArmorReductionOnHit", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.DamageOverflow | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 30, "Shattering Justice.");
			Reg("IncreaseDamageOnMultiKill", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.DamageOverflow, ScalingMode.SoftCap, 40, "Chronic Expansion.");
			Reg("RepeatHeal", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.HealingRecursion | UnsafeBehaviorFlags.InfiniteUptime, ScalingMode.Hyperbolic, 15, "Corpsebloom.");
			Reg("BarrierOnOverHeal", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.HealingRecursion | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 40, "Aegis.");
			Reg("IncreaseHealing", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.HealingRecursion, ScalingMode.SoftCap, 40, "Rejuvenation Rack.");
			Reg("HealOnCrit", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.AttackSpeed | UnsafeBehaviorFlags.HealingRecursion, ScalingMode.SoftCap, 50, "Harvester's Scythe.");
			Reg("ExtraLife", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.HealingRecursion | UnsafeBehaviorFlags.InfiniteUptime, ScalingMode.Hyperbolic, 10, "Dio's Best Friend.");
			Reg("ExtraLifeVoid", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.HealingRecursion | UnsafeBehaviorFlags.InfiniteUptime, ScalingMode.Hyperbolic, 10, "Pluripotent Larva.");
			Reg("CaptainDefenseMatrix", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProjectileSpam | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 20, "Defensive Microbots.");
			Reg("RoboBallBuddy", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.AiBreaking | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 20, "Empathy Cores.");
			Reg("MinorConstructOnKill", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.AiBreaking, ScalingMode.SoftCap, 25, "Defense Nucleus.");
			Reg("BeetleGland", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.AiBreaking, ScalingMode.SoftCap, 30, "Queen's Gland.");
			Reg("MonstersOnShrineUse", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.AiBreaking | UnsafeBehaviorFlags.ServerPerformanceRisk, ScalingMode.SoftCap, 25, "Defiant Gouge.");
			Reg("RandomDamageZone", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.MultiplayerDesyncRisk, ScalingMode.SoftCap, 20, "Mercurial Rachis.");
			Reg("GoldOnHit", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion | UnsafeBehaviorFlags.MultiplayerDesyncRisk, ScalingMode.SoftCap, 25, "Brittle Crown.");
			Reg("GoldOnHurt", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.ProcRecursion, ScalingMode.SoftCap, 40, "Roll of Pennies.");
			Reg("FocusConvergence", UnsafeRiskTier.Unsafe, UnsafeBehaviorFlags.MultiplayerDesyncRisk, ScalingMode.SoftCap, 15, "Focused Convergence.");
			Reg("CritGlasses", UnsafeRiskTier.Mild, UnsafeBehaviorFlags.ProcRecursion, ScalingMode.SoftCap, null, "Lens-Maker's Glasses: crit chance saturates; soft-cap recommended.");
			Reg("CritGlassesVoid", UnsafeRiskTier.Mild, UnsafeBehaviorFlags.ProcRecursion, ScalingMode.SoftCap, null, "Lost Seer's Lenses.");
			Reg("CritDamage", UnsafeRiskTier.Mild, UnsafeBehaviorFlags.DamageOverflow, ScalingMode.SoftCap, null, "Laser Scope.");
			Reg("Crowbar", UnsafeRiskTier.Mild, UnsafeBehaviorFlags.DamageOverflow, ScalingMode.SoftCap, null, "Crowbar: damage vs high-health.");
			Reg("NearbyDamageBonus", UnsafeRiskTier.Mild, UnsafeBehaviorFlags.DamageOverflow, ScalingMode.SoftCap, null, "Focus Crystal.");
			Reg("BossDamageBonus", UnsafeRiskTier.Mild, UnsafeBehaviorFlags.DamageOverflow, ScalingMode.SoftCap, null, "Armor-Piercing Rounds.");
			Reg("SecondarySkillMagazine", UnsafeRiskTier.Mild, UnsafeBehaviorFlags.ProcRecursion, Scalin