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 _artifactActiveQuery; private bool _loggedLinearSanitize; public ExponentialGrantEvaluator(ModConfig config, ItemConfigRegistry registry, ManualLogSource log, Func 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 _filterEntries = new HashSet(); private readonly Dictionary _stackCaps = new Dictionary(); private readonly Dictionary _exponentialBases = new Dictionary(); 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 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 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((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(); 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 ParseFilterListEntries(string raw, ManualLogSource log, string diagnosticsName = "Blacklist / Whitelist identifiers") { return KeyValueListParser.ParseDelimitedList(raw, log, diagnosticsName); } public static Dictionary ParseStackOverrides(string raw, ManualLogSource log) { return KeyValueListParser.ParseKeyValueMap(raw, log, "ItemStackOverrides"); } public static Dictionary ParseExponentialBaseOverrides(string raw, ManualLogSource log) { return KeyValueListParser.ParseKeyValueMap(raw, log, "ItemExponentialBases"); } public static List 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 ParseDelimitedList(string raw, ManualLogSource log, string optionName) { List list = new List(); 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 ParseKeyValueMap(string raw, ManualLogSource log, string optionName) { Dictionary dictionary = new Dictionary(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 Enabled { get; } public ConfigEntry RequireArtifact { get; } public ConfigEntry LinearPickupMultiplier { get; } public ConfigEntry ExponentialBase { get; } public ConfigEntry GlobalMaxItemStacks { get; } public ConfigEntry FilterIsWhitelistMode { get; } public ConfigEntry FilterItemIdentifiers { get; } public ConfigEntry ItemStackOverrideSpec { get; } public ConfigEntry ItemExponentialBaseSpec { get; } public ConfigEntry VerboseLogging { get; } public ConfigFile Bindings { get; } internal ModConfig(ConfigFile configFile) { Bindings = configFile ?? throw new ArgumentNullException("configFile"); Enabled = Bindings.Bind("0_Global", "Enabled", true, "Master enable for exponential pickups. When false, the plugin restores vanilla grant counts."); RequireArtifact = Bindings.Bind("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("0_Global", "LinearPickupMultiplier", 1, "Multiply the incoming vanilla pickup quantity by this linear factor before exponentials."); ExponentialBase = Bindings.Bind("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("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("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("1_Filter", "FilterItemIdentifiers", string.Empty, "Semicolon-, comma-, or newline-separated identifiers (ItemDef.name or catalog names). Examples: BoostSyringe; PaulsGoatHoof; Feather"); ItemStackOverrideSpec = Bindings.Bind("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("2_ItemStackOverrides", "ItemExponentialBaseSpec", string.Empty, "Optional per-item exponential bases (fallback to Global ExponentialBase if missing).\nSyntax: BoostSyringe=3; PaulsGoatHoof=2"); VerboseLogging = Bindings.Bind("9_Debug", "VerboseLogging", false, "Extra BepInEx logging for blacklist/whitelist hits, lookups, overrides, and grant computations."); } } }