Decompiled source of ExponentialItemsReloaded v1.2.0

plugins\ExponentialItemsReloaded.dll

Decompiled 3 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExponentialItemsReloaded.Configuration;
using ExponentialItemsReloaded.Content;
using ExponentialItemsReloaded.Services;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using R2API;
using RoR2;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ExponentialItemsReloaded")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ExponentialItemsReloaded")]
[assembly: AssemblyTitle("ExponentialItemsReloaded")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ExponentialItemsReloaded
{
	[BepInPlugin("CryptidLabs.ExponentialItemsReloaded", "ExponentialItemsReloaded", "1.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class ExponentialItemsReloadedPlugin : BaseUnityPlugin
	{
		internal const string PluginGUID = "CryptidLabs.ExponentialItemsReloaded";

		internal const string R2APICoreGUID = "com.riskofthunder.r2api";

		internal const string PluginName = "ExponentialItemsReloaded";

		internal const string PluginVersion = "1.2.0";

		private Harmony _harmony;

		internal static ExponentialItemsReloadedPlugin Instance { get; private set; }

		internal ModConfig PluginConfig { get; private set; }

		internal ItemConfigRegistry ItemCatalogSettings { get; private set; }

		internal ExponentialGrantEvaluator GrantEvaluator { get; private set; }

		private void Awake()
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			Instance = this;
			PluginConfig = new ModConfig(((BaseUnityPlugin)this).Config);
			ItemCatalogSettings = new ItemConfigRegistry(PluginConfig, ((BaseUnityPlugin)this).Logger);
			GrantEvaluator = new ExponentialGrantEvaluator(PluginConfig, ItemCatalogSettings, ((BaseUnityPlugin)this).Logger, () => ExponentArtifact.IsEnabledForCurrentRun());
			ExponentArtifact.Register(((BaseUnityPlugin)this).Logger);
			HookRoR2Lifecycle();
			PluginConfig.Bindings.SettingChanged += OnAnySettingChanged;
			_harmony = new Harmony("CryptidLabs.ExponentialItemsReloaded");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"ExponentialItemsReloaded 1.2.0 loaded — awaiting ItemCatalog initialization for blacklist/override resolution.");
		}

		private void HookRoR2Lifecycle()
		{
			RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(HandleRoR2Loaded));
		}

		private void OnDestroy()
		{
			RoR2Application.onLoad = (Action)Delegate.Remove(RoR2Application.onLoad, new Action(HandleRoR2Loaded));
			if (PluginConfig != null)
			{
				PluginConfig.Bindings.SettingChanged -= OnAnySettingChanged;
			}
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Instance = null;
		}

		private void HandleRoR2Loaded()
		{
			try
			{
				ItemCatalogSettings.RequestCatalogRebuild();
				ItemCatalogSettings.RebuildCatalogCachesIfNeeded();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"RoR2 onLoad invoked — exponential filter dictionaries refreshed.");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Failed while rebuilding catalog caches after RoR2 load: {arg}");
			}
		}

		private void OnAnySettingChanged(object sender, SettingChangedEventArgs e)
		{
			try
			{
				ItemCatalogSettings.RequestCatalogRebuild();
				ItemCatalogSettings.RebuildCatalogCachesIfNeeded();
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Config refresh failed: {arg}");
			}
		}
	}
}
namespace ExponentialItemsReloaded.Services
{
	public sealed class ExponentialGrantEvaluator
	{
		private readonly ModConfig _config;

		private readonly ItemConfigRegistry _registry;

		private readonly ManualLogSource _log;

		private readonly Func<bool> _artifactActiveQuery;

		private bool _loggedLinearSanitize;

		public ExponentialGrantEvaluator(ModConfig config, ItemConfigRegistry registry, ManualLogSource log, Func<bool> artifactActiveQuery)
		{
			_config = config ?? throw new ArgumentNullException("config");
			_registry = registry ?? throw new ArgumentNullException("registry");
			_log = log ?? throw new ArgumentNullException("log");
			_artifactActiveQuery = artifactActiveQuery ?? throw new ArgumentNullException("artifactActiveQuery");
		}

		public int EvaluateAdjustedCount(Inventory inventory, ItemDef itemDef, int vanillaCount)
		{
			if (!NetworkServer.active)
			{
				return vanillaCount;
			}
			if (!_config.Enabled.Value)
			{
				return vanillaCount;
			}
			if (!Object.op_Implicit((Object)(object)inventory) || !Object.op_Implicit((Object)(object)itemDef))
			{
				return Math.Max(vanillaCount, 0);
			}
			try
			{
				return EvaluateCore(inventory, itemDef, vanillaCount);
			}
			catch (Exception arg)
			{
				_log.LogError((object)$"Safeguard fallback while evaluating exponential pickup for {((itemDef != null) ? ((Object)itemDef).name : null)}: {arg}");
				return Math.Max(vanillaCount, 0);
			}
		}

		private int EvaluateCore(Inventory inventory, ItemDef itemDef, int vanillaCount)
		{
			_registry.RebuildCatalogCachesIfNeeded();
			int num = Math.Max(vanillaCount, 0);
			int itemCountEffective = inventory.GetItemCountEffective(itemDef);
			int globalCap = Math.Max(1, _config.GlobalMaxItemStacks.Value);
			int applicableMaxStacks = _registry.GetApplicableMaxStacks(itemDef, globalCap);
			int headroom = Math.Max(0, applicableMaxStacks - itemCountEffective);
			if (_config.RequireArtifact.Value && !_artifactActiveQuery())
			{
				if (_config.VerboseLogging.Value)
				{
					_log.LogDebug((object)$"Artifact disabled — leaving vanilla grant x{num} for {((Object)itemDef).name}.");
				}
				return ClampToHeadroom(num, headroom);
			}
			if (!_registry.ShouldReceiveExponential(itemDef))
			{
				if (_config.VerboseLogging.Value)
				{
					_log.LogDebug((object)$"Filter blocked exponentials for {((Object)itemDef).name}; vanilla x{num}.");
				}
				return ClampToHeadroom(num, headroom);
			}
			int num2 = _config.LinearPickupMultiplier.Value;
			if (num2 <= 0)
			{
				if (!_loggedLinearSanitize)
				{
					_log.LogWarning((object)"LinearPickupMultiplier must be > 0 — forcing 1.");
					_loggedLinearSanitize = true;
				}
				num2 = 1;
			}
			int globalExponentBase = Math.Max(1, _config.ExponentialBase.Value);
			globalExponentBase = _registry.GetApplicableExponentialBase(itemDef, globalExponentBase);
			double y = itemCountEffective;
			double num3 = Math.Pow(globalExponentBase, y);
			if (double.IsNaN(num3) || double.IsInfinity(num3) || num3 > 2147483647.0)
			{
				num3 = 2147483647.0;
			}
			double num4 = (double)num * (double)num2 * num3;
			if (double.IsNaN(num4) || double.IsInfinity(num4) || num4 > 2147483647.0)
			{
				num4 = 2147483647.0;
			}
			int val = (int)Math.Round(num4);
			val = Math.Max(num, val);
			val = ClampToHeadroom(val, headroom);
			if (_config.VerboseLogging.Value)
			{
				_log.LogDebug((object)$"Exponential grant for {((Object)itemDef).name}: stacks={itemCountEffective}, base={globalExponentBase}, linear={num2}, rawVanilla={num}, final={val}, cap={applicableMaxStacks}.");
			}
			return val;
		}

		private static int ClampToHeadroom(int amount, int headroom)
		{
			if (headroom <= 0)
			{
				return 0;
			}
			return Math.Min(Math.Max(amount, 0), headroom);
		}
	}
	public sealed class ItemConfigRegistry
	{
		private readonly ManualLogSource _log;

		private readonly ModConfig _config;

		private readonly HashSet<ItemDef> _filterEntries = new HashSet<ItemDef>();

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

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

		private bool _catalogRebuildQueued = true;

		public ItemConfigRegistry(ModConfig config, ManualLogSource log)
		{
			_config = config ?? throw new ArgumentNullException("config");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public void RebuildCatalogCaches()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (ItemCatalog.allItemDefs.Length == 0)
			{
				_log.LogWarning((object)"ItemCatalog.allItemDefs empty — catalogs not initialized yet?");
				return;
			}
			_filterEntries.Clear();
			_stackCaps.Clear();
			_exponentialBases.Clear();
			foreach (string item in KeyValueListParser.ParseDelimitedList(_config.FilterItemIdentifiers.Value, _log, "FilterItemIdentifiers"))
			{
				if (TryResolveItemDef(item, out var def, "FilterItemIdentifiers") && _filterEntries.Add(def) && _config.VerboseLogging.Value)
				{
					_log.LogDebug((object)("Registered filter entry (" + DescribeMode() + "): \"" + item + "\" -> " + ((def != null) ? ((Object)def).name : null)));
				}
			}
			foreach (KeyValuePair<string, string> item2 in KeyValueListParser.ParseKeyValueMap(_config.ItemStackOverrideSpec.Value, _log, "ItemStackOverrideSpec"))
			{
				if (TryResolveItemDef(item2.Key, out var def2, "ItemStackOverrideSpec") && TryReadPositiveInt(item2.Key, item2.Value, out var value, "ItemStackOverrideSpec"))
				{
					_stackCaps[def2] = value;
					if (_config.VerboseLogging.Value)
					{
						_log.LogInfo((object)$"Applied stack override \"{((Object)def2).name}\" -> max {value}");
					}
				}
			}
			foreach (KeyValuePair<string, string> item3 in KeyValueListParser.ParseKeyValueMap(_config.ItemExponentialBaseSpec.Value, _log, "ItemExponentialBaseSpec"))
			{
				if (!TryResolveItemDef(item3.Key, out var def3, "ItemExponentialBaseSpec") || !TryReadPositiveInt(item3.Key, item3.Value, out var value2, "ItemExponentialBaseSpec"))
				{
					continue;
				}
				if (value2 < 2)
				{
					_log.LogWarning((object)string.Format("[{0}] Exponential base must be >= 2 for \"{1}\" (got {2}); skipped.", "ItemExponentialBaseSpec", item3.Key, value2));
					continue;
				}
				_exponentialBases[def3] = value2;
				if (_config.VerboseLogging.Value)
				{
					_log.LogInfo((object)$"Applied custom exponential base for \"{((Object)def3).name}\" -> {value2}");
				}
			}
			_catalogRebuildQueued = false;
			_log.LogInfo((object)$"ExponentialItemsReloaded config cache rebuilt: filter entries={_filterEntries.Count}, stack overrides={_stackCaps.Count}, exp bases={_exponentialBases.Count}.");
		}

		public void RequestCatalogRebuild()
		{
			_catalogRebuildQueued = true;
		}

		public void RebuildCatalogCachesIfNeeded()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (_catalogRebuildQueued && ItemCatalog.allItemDefs.Length != 0)
			{
				RebuildCatalogCaches();
			}
		}

		private string DescribeMode()
		{
			if (!_config.FilterIsWhitelistMode.Value)
			{
				return "blacklist";
			}
			return "whitelist";
		}

		public bool ShouldReceiveExponential(ItemDef itemDef)
		{
			if (!Object.op_Implicit((Object)(object)itemDef))
			{
				return false;
			}
			bool flag = _filterEntries.Contains(itemDef);
			bool flag2 = (_config.FilterIsWhitelistMode.Value ? flag : (!flag));
			if (!flag2 && _config.VerboseLogging.Value)
			{
				_log.LogDebug((object)("Filter (" + DescribeMode() + ") withheld exponentials from item \"" + ((Object)itemDef).name + "\"."));
			}
			return flag2;
		}

		public bool TryGetStackOverride(ItemDef itemDef, out int cap)
		{
			return _stackCaps.TryGetValue(itemDef, out cap);
		}

		public bool TryGetItemOverride(ItemDef itemDef, out int maxStacksCap)
		{
			return TryGetStackOverride(itemDef, out maxStacksCap);
		}

		public int GetApplicableMaxStacks(ItemDef itemDef, int globalCap)
		{
			int result = SanitizeGlobalCap(globalCap);
			if (!_stackCaps.TryGetValue(itemDef, out var value))
			{
				return result;
			}
			return Math.Max(1, value);
		}

		public int GetApplicableExponentialBase(ItemDef itemDef, int globalExponentBase)
		{
			if (!_exponentialBases.TryGetValue(itemDef, out var value))
			{
				return globalExponentBase;
			}
			return value;
		}

		private static int SanitizeGlobalCap(int globalCap)
		{
			if (globalCap <= 0)
			{
				return 1;
			}
			return Math.Min(globalCap, 2147479551);
		}

		public unsafe bool TryResolveItemDef(string key, out ItemDef def, string sourceOption)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			def = null;
			if (string.IsNullOrWhiteSpace(key))
			{
				return false;
			}
			string text = key.Trim();
			for (int i = 0; i < ItemCatalog.allItemDefs.Length; i++)
			{
				ItemDef val = (ItemDef)Unsafe.Read<object>((void*)ItemCatalog.allItemDefs[i]);
				if (Object.op_Implicit((Object)(object)val))
				{
					if (string.Equals(((Object)val).name, text, StringComparison.OrdinalIgnoreCase))
					{
						def = val;
						return true;
					}
					if (!string.IsNullOrEmpty(val.nameToken) && string.Equals(val.nameToken, text, StringComparison.OrdinalIgnoreCase))
					{
						def = val;
						return true;
					}
					string text2 = NormalizeNameToken(val.nameToken);
					if (!string.IsNullOrEmpty(text2) && string.Equals(text2, text, StringComparison.OrdinalIgnoreCase))
					{
						def = val;
						return true;
					}
				}
			}
			_log.LogWarning((object)("[" + sourceOption + "] Unknown item identifier \"" + text + "\". It may be typo'd, unloaded mod content, or locked behind DLC you do not own."));
			return false;
		}

		private static string NormalizeNameToken(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return null;
			}
			if (token.StartsWith("ITEM_", StringComparison.OrdinalIgnoreCase) && token.EndsWith("_NAME", StringComparison.OrdinalIgnoreCase))
			{
				int num = token.Length - 10;
				if (num > 0)
				{
					return token.Substring(5, num);
				}
			}
			return token;
		}

		private bool TryReadPositiveInt(string contextKey, string rawNumber, out int value, string sourceOption)
		{
			value = 0;
			if (!int.TryParse(rawNumber.Trim(), out var result))
			{
				_log.LogWarning((object)("[" + sourceOption + "] \"" + contextKey + "\" has non-integer value \"" + rawNumber + "\"."));
				return false;
			}
			if (result <= 0)
			{
				_log.LogWarning((object)$"[{sourceOption}] \"{contextKey}\" must be positive (got {result}); skipped.");
				return false;
			}
			value = result;
			return true;
		}
	}
}
namespace ExponentialItemsReloaded.Hooks
{
	[HarmonyPatch(typeof(Inventory), "GiveItem", new Type[]
	{
		typeof(ItemDef),
		typeof(int)
	})]
	internal static class InventoryGiveWithItemDefPatch
	{
		[HarmonyPrefix]
		internal static void Prefix(Inventory __instance, ItemDef itemDef, ref int count)
		{
			ExponentialItemsReloadedPlugin instance = ExponentialItemsReloadedPlugin.Instance;
			if (instance?.GrantEvaluator != null)
			{
				count = instance.GrantEvaluator.EvaluateAdjustedCount(__instance, itemDef, count);
			}
		}
	}
}
namespace ExponentialItemsReloaded.Content
{
	public static class ExponentArtifact
	{
		public static ArtifactDef Definition { get; private set; }

		internal static void Register(ManualLogSource log)
		{
			try
			{
				Definition = ScriptableObject.CreateInstance<ArtifactDef>();
				Definition.cachedName = "ArtifactOfExponents";
				Definition.nameToken = "EXPONENTITEMSRELOADED_ARTIFACT_NAME";
				Definition.descriptionToken = "EXPONENTITEMSRELOADED_ARTIFACT_DESC";
				ArtifactDef command = Artifacts.Command;
				if (!Object.op_Implicit((Object)(object)command) || !Object.op_Implicit((Object)(object)command.smallIconSelectedSprite) || !Object.op_Implicit((Object)(object)command.smallIconDeselectedSprite))
				{
					log.LogWarning((object)"ExponentArtifact could not inherit icons from ArtifactOfCommand — registration aborted.");
					Definition = null;
					return;
				}
				Definition.smallIconSelectedSprite = command.smallIconSelectedSprite;
				Definition.smallIconDeselectedSprite = command.smallIconDeselectedSprite;
				if (!ContentAddition.AddArtifactDef(Definition))
				{
					log.LogWarning((object)"ContentAddition rejected ArtifactDef (catalog already initialized?). Exponential boosts may require RequireArtifact=false.");
				}
				LanguageAPI.Add(Definition.nameToken, "Artifact of Exponents");
				LanguageAPI.Add(Definition.descriptionToken, "Item pickups grant exponentially increasing stacks.");
			}
			catch (Exception arg)
			{
				log.LogError((object)$"ExponentArtifact bootstrap failed — artifact gating unavailable: {arg}");
				Definition = null;
			}
		}

		public static bool IsEnabledForCurrentRun()
		{
			if (!Object.op_Implicit((Object)(object)Definition))
			{
				return false;
			}
			if (Object.op_Implicit((Object)(object)RunArtifactManager.instance))
			{
				return RunArtifactManager.instance.IsArtifactEnabled(Definition);
			}
			return false;
		}
	}
}
namespace ExponentialItemsReloaded.Configuration
{
	public static class ConfigParser
	{
		public static List<string> ParseFilterListEntries(string raw, ManualLogSource log, string diagnosticsName = "Blacklist / Whitelist identifiers")
		{
			return KeyValueListParser.ParseDelimitedList(raw, log, diagnosticsName);
		}

		public static Dictionary<string, string> ParseStackOverrides(string raw, ManualLogSource log)
		{
			return KeyValueListParser.ParseKeyValueMap(raw, log, "ItemStackOverrides");
		}

		public static Dictionary<string, string> ParseExponentialBaseOverrides(string raw, ManualLogSource log)
		{
			return KeyValueListParser.ParseKeyValueMap(raw, log, "ItemExponentialBases");
		}

		public static List<string> ParseBlacklist(string raw, ManualLogSource log)
		{
			return ParseFilterListEntries(raw, log, "Blacklist identifiers");
		}
	}
	public static class KeyValueListParser
	{
		private static readonly char[] ListSeparators = new char[4] { ';', ',', '\n', '\r' };

		public static List<string> ParseDelimitedList(string raw, ManualLogSource log, string optionName)
		{
			List<string> list = new List<string>();
			if (string.IsNullOrWhiteSpace(raw))
			{
				return list;
			}
			string[] array = raw.Split(ListSeparators, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					list.Add(text);
				}
			}
			return list;
		}

		public static Dictionary<string, string> ParseKeyValueMap(string raw, ManualLogSource log, string optionName)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrWhiteSpace(raw))
			{
				return dictionary;
			}
			string[] array = raw.Split(ListSeparators, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0)
				{
					continue;
				}
				int num = text.IndexOf('=');
				if (num <= 0 || num >= text.Length - 1)
				{
					if (log != null)
					{
						log.LogWarning((object)("[" + optionName + "] Ignoring invalid entry (expected Key=Value): \"" + text + "\""));
					}
					continue;
				}
				string text2 = text.Substring(0, num).Trim();
				string value = text.Substring(num + 1).Trim();
				if (text2.Length == 0)
				{
					if (log != null)
					{
						log.LogWarning((object)("[" + optionName + "] Ignoring entry with empty key: \"" + text + "\""));
					}
				}
				else
				{
					dictionary[text2] = value;
				}
			}
			return dictionary;
		}
	}
	public sealed class ModConfig
	{
		public ConfigEntry<bool> Enabled { get; }

		public ConfigEntry<bool> RequireArtifact { get; }

		public ConfigEntry<int> LinearPickupMultiplier { get; }

		public ConfigEntry<int> ExponentialBase { get; }

		public ConfigEntry<int> GlobalMaxItemStacks { get; }

		public ConfigEntry<bool> FilterIsWhitelistMode { get; }

		public ConfigEntry<string> FilterItemIdentifiers { get; }

		public ConfigEntry<string> ItemStackOverrideSpec { get; }

		public ConfigEntry<string> ItemExponentialBaseSpec { get; }

		public ConfigEntry<bool> VerboseLogging { get; }

		public ConfigFile Bindings { get; }

		internal ModConfig(ConfigFile configFile)
		{
			Bindings = configFile ?? throw new ArgumentNullException("configFile");
			Enabled = Bindings.Bind<bool>("0_Global", "Enabled", true, "Master enable for exponential pickups. When false, the plugin restores vanilla grant counts.");
			RequireArtifact = Bindings.Bind<bool>("0_Global", "RequireArtifact", true, "If true, exponential math only applies when Artifact of Exponents is enabled on the server for the current run.");
			LinearPickupMultiplier = Bindings.Bind<int>("0_Global", "LinearPickupMultiplier", 1, "Multiply the incoming vanilla pickup quantity by this linear factor before exponentials.");
			ExponentialBase = Bindings.Bind<int>("0_Global", "ExponentialBase", 2, "Integer base raised to power of existing stacks when computing bonus pickup size (2 mimics legacy 1-2-4-8 pacing).");
			GlobalMaxItemStacks = Bindings.Bind<int>("0_Global", "GlobalMaxItemStacks", 4096, "Hard cap applied to inventories for exponential grants when no override is defined (also bounds math to avoid overflows).");
			FilterIsWhitelistMode = Bindings.Bind<bool>("1_Filter", "FilterIsWhitelistMode", false, "If false: identifiers listed in FilterItemIdentifiers are blacklisted (no exponentials).\nIf true: only those identifiers receive exponentials (whitelist mode).");
			FilterItemIdentifiers = Bindings.Bind<string>("1_Filter", "FilterItemIdentifiers", string.Empty, "Semicolon-, comma-, or newline-separated identifiers (ItemDef.name or catalog names). Examples: BoostSyringe; PaulsGoatHoof; Feather");
			ItemStackOverrideSpec = Bindings.Bind<string>("2_ItemStackOverrides", "ItemStackOverrideSpec", string.Empty, "Per-item stack caps overriding GlobalMaxItemStacks.\nSyntax: BoostSyringe=1024; PaulsGoatHoof=32; LensMakersGlasses=64\nInvalid rows are skipped with warnings.");
			ItemExponentialBaseSpec = Bindings.Bind<string>("2_ItemStackOverrides", "ItemExponentialBaseSpec", string.Empty, "Optional per-item exponential bases (fallback to Global ExponentialBase if missing).\nSyntax: BoostSyringe=3; PaulsGoatHoof=2");
			VerboseLogging = Bindings.Bind<bool>("9_Debug", "VerboseLogging", false, "Extra BepInEx logging for blacklist/whitelist hits, lookups, overrides, and grant computations.");
		}
	}
}