Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ConfigurableStageCredit v0.3.5
plugins\ConfigurableStageCredit\ConfigurableStageCredit.dll
Decompiled 16 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using RoR2; using UnityEngine; using UnityEngine.Networking; using UnityEngine.SceneManagement; [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("ConfigurableStageCredit")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Configure per-stage multipliers for Risk of Rain 2 enemy and interactable spawn credits.")] [assembly: AssemblyFileVersion("0.3.5.0")] [assembly: AssemblyInformationalVersion("0.3.5")] [assembly: AssemblyProduct("ConfigurableStageCredit")] [assembly: AssemblyTitle("ConfigurableStageCredit")] [assembly: AssemblyVersion("0.3.5.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ConfigurableStageCredit { [BepInPlugin("com.configurablestagecredit.plugin", "ConfigurableStageCredit", "0.3.5")] public sealed class Plugin : BaseUnityPlugin { private sealed class CreditPrintSnapshot { internal readonly int StageNumber; internal bool HasEnemyCredit; internal bool HasLootCredit; internal double EnemyVanillaCredit; internal double EnemyNewCredit; internal float EnemyMultiplier; internal double LootVanillaCredit; internal double LootNewCredit; internal float LootMultiplier; internal CreditPrintSnapshot(int stageNumber) { StageNumber = stageNumber; } } public const string PluginGuid = "com.configurablestagecredit.plugin"; public const string PluginName = "ConfigurableStageCredit"; public const string PluginVersion = "0.3.5"; internal static ManualLogSource ModLog; private static readonly FieldInfo MonsterCreditField = typeof(SceneDirector).GetField("monsterCredit", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private CreditSettings _enemyCredits; private CreditSettings _lootCredits; private ConfigEntry<bool> _printCreditOnStageStart; private readonly Dictionary<int, CreditPrintSnapshot> _pendingCreditPrints = new Dictionary<int, CreditPrintSnapshot>(); private readonly HashSet<int> _reportedCreditPrints = new HashSet<int>(); private void Awake() { ModLog = ((BaseUnityPlugin)this).Logger; _enemyCredits = new CreditSettings(((BaseUnityPlugin)this).Config, "1-Enemy Credit"); _lootCredits = new CreditSettings(((BaseUnityPlugin)this).Config, "2-Loot Credit"); _printCreditOnStageStart = ((BaseUnityPlugin)this).Config.Bind<bool>("0-General", "PrintCreditOnStageStart", false, "When enabled, print the enemy and loot credit multipliers, original budgets, and modified budgets for every populated map."); SceneDirector.onPrePopulateMonstersSceneServer += OnPrePopulateMonsters; SceneDirector.onPrePopulateSceneServer += OnPrePopulateInteractables; if (MonsterCreditField == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not find SceneDirector.monsterCredit. Enemy credit scaling will be disabled; loot credit scaling can still work."); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded. Enemy and loot credit multipliers are configured independently in the BepInEx config file."); } private void OnDestroy() { SceneDirector.onPrePopulateMonstersSceneServer -= OnPrePopulateMonsters; SceneDirector.onPrePopulateSceneServer -= OnPrePopulateInteractables; } private static int GetCurrentStageNumber() { Run instance = Run.instance; if (!((Object)(object)instance == (Object)null)) { return Math.Max(1, instance.stageClearCount + 1); } return 1; } private void OnPrePopulateMonsters(SceneDirector director) { if (NetworkServer.active && !((Object)(object)director == (Object)null) && !(MonsterCreditField == null)) { ScaleReflectedCredit(director, MonsterCreditField, _enemyCredits, "enemy"); } } private void OnPrePopulateInteractables(SceneDirector director) { if (NetworkServer.active && !((Object)(object)director == (Object)null)) { int currentStageNumber = GetCurrentStageNumber(); float multiplier = _lootCredits.GetMultiplier(currentStageNumber); int interactableCredit = director.interactableCredit; int num = (director.interactableCredit = ScaleIntegerCredit(interactableCredit, multiplier)); ModLog.LogDebug((object)string.Format(CultureInfo.InvariantCulture, "Stage {0}: loot credit {1} -> {2} (x{3:0.###}).", currentStageNumber, interactableCredit, num, multiplier)); RecordCreditForChat(director, currentStageNumber, isEnemy: false, interactableCredit, num, multiplier); } } private void ScaleReflectedCredit(SceneDirector director, FieldInfo field, CreditSettings settings, string label) { try { object value = field.GetValue(director); if (value != null) { int currentStageNumber = GetCurrentStageNumber(); double num = Convert.ToDouble(value, CultureInfo.InvariantCulture); float multiplier = settings.GetMultiplier(currentStageNumber); object value2 = ConvertCreditToFieldType(Math.Max(0.0, num * (double)multiplier), field.FieldType); field.SetValue(director, value2); double num2 = Convert.ToDouble(value2, CultureInfo.InvariantCulture); ModLog.LogDebug((object)string.Format(CultureInfo.InvariantCulture, "Stage {0}: {1} credit {2:0.##} -> {3:0.##} (x{4:0.###}).", currentStageNumber, label, num, num2, multiplier)); RecordCreditForChat(director, currentStageNumber, isEnemy: true, num, num2, multiplier); } } catch (Exception ex) { ModLog.LogError((object)("Failed to scale enemy spawn credit: " + ex)); } } private void RecordCreditForChat(SceneDirector director, int stage, bool isEnemy, double vanillaCredit, double newCredit, float multiplier) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (_printCreditOnStageStart == null || !_printCreditOnStageStart.Value || (Object)(object)director == (Object)null) { return; } Scene scene = ((Component)director).gameObject.scene; int handle = ((Scene)(ref scene)).handle; if (!_reportedCreditPrints.Contains(handle)) { if (!_pendingCreditPrints.TryGetValue(handle, out var value)) { value = new CreditPrintSnapshot(stage); _pendingCreditPrints.Add(handle, value); } if (isEnemy) { value.EnemyVanillaCredit = vanillaCredit; value.EnemyNewCredit = newCredit; value.EnemyMultiplier = multiplier; value.HasEnemyCredit = true; } else { value.LootVanillaCredit = vanillaCredit; value.LootNewCredit = newCredit; value.LootMultiplier = multiplier; value.HasLootCredit = true; } if (value.HasEnemyCredit && value.HasLootCredit) { _reportedCreditPrints.Add(handle); _pendingCreditPrints.Remove(handle); string message = string.Format(CultureInfo.InvariantCulture, "<style=cEvent>[ConfigurableStageCredit]</style> Stage {0}\n Enemy x{1:0.###}: vanilla {2:0.##}, new {3:0.##}\n Loot x{4:0.###}: vanilla {5:0.##}, new {6:0.##}", value.StageNumber, value.EnemyMultiplier, value.EnemyVanillaCredit, value.EnemyNewCredit, value.LootMultiplier, value.LootVanillaCredit, value.LootNewCredit); ((MonoBehaviour)this).StartCoroutine(BroadcastCreditReportAfterSceneLoad(director, message)); } } } private IEnumerator BroadcastCreditReportAfterSceneLoad(SceneDirector director, string message) { yield return (object)new WaitForSecondsRealtime(1f); if (!NetworkServer.active || (Object)(object)director == (Object)null) { yield break; } Scene val = ((Component)director).gameObject.scene; int handle = ((Scene)(ref val)).handle; val = SceneManager.GetActiveScene(); if (handle != ((Scene)(ref val)).handle) { yield break; } try { Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage { baseToken = message }); } catch (Exception ex) { ModLog.LogWarning((object)("Could not print the scene credit report to chat: " + ex.Message)); } } private static object ConvertCreditToFieldType(double value, Type fieldType) { value = Math.Max(0.0, value); if (fieldType == typeof(float)) { return (float)Math.Min(value, 3.4028234663852886E+38); } if (fieldType == typeof(double)) { return value; } if (fieldType == typeof(int)) { return (int)Math.Min(2147483647.0, Math.Round(value, MidpointRounding.AwayFromZero)); } if (fieldType == typeof(uint)) { return (uint)Math.Min(4294967295.0, Math.Round(value, MidpointRounding.AwayFromZero)); } if (fieldType == typeof(long)) { if (value >= 9.223372036854776E+18) { return long.MaxValue; } return (long)Math.Round(value, MidpointRounding.AwayFromZero); } if (fieldType == typeof(ulong)) { if (value >= 1.8446744073709552E+19) { return ulong.MaxValue; } return (ulong)Math.Round(value, MidpointRounding.AwayFromZero); } return Convert.ChangeType(value, fieldType, CultureInfo.InvariantCulture); } private static int ScaleIntegerCredit(int credit, float multiplier) { double value = Math.Max(0.0, (double)credit * (double)multiplier); return (int)Math.Min(2147483647.0, Math.Round(value, MidpointRounding.AwayFromZero)); } } internal sealed class CreditSettings { private readonly ConfigFile _config; private readonly ConfigEntry<float> _startingMultiplier; private readonly ConfigEntry<float> _stageIncrement; private readonly ConfigEntry<float> _loopIncrement; private readonly ConfigEntry<float> _minimumMultiplier; private readonly ConfigEntry<float> _maximumMultiplier; private bool _normalizing; internal CreditSettings(ConfigFile config, string section) { _config = config; _startingMultiplier = config.Bind<float>(section, "StartingMultiplier", 1f, "Multiplier for stage 1. 1.15 means 15% more credits than the vanilla budget. If below MinimumMultiplier, it is set to MinimumMultiplier."); _stageIncrement = config.Bind<float>(section, "StageIncrement", 0f, "Additive change to the multiplier between stages, except when entering a new 5-stage loop."); _loopIncrement = config.Bind<float>(section, "LoopIncrement", 0f, "Additive change to the multiplier when entering a new loop (stages 6, 11, 16, etc.), replacing StageIncrement for that transition."); _minimumMultiplier = config.Bind<float>(section, "MinimumMultiplier", 0f, "Lower bound for the calculated multiplier. Negative values are reset to 0."); _maximumMultiplier = config.Bind<float>(section, "MaximumMultiplier", 10f, "Upper bound for the calculated multiplier. If below MinimumMultiplier, it is set to MinimumMultiplier."); _startingMultiplier.SettingChanged += OnSettingChanged; _stageIncrement.SettingChanged += OnSettingChanged; _loopIncrement.SettingChanged += OnSettingChanged; _minimumMultiplier.SettingChanged += OnSettingChanged; _maximumMultiplier.SettingChanged += OnSettingChanged; NormalizeSettings(); } internal float GetMultiplier(int stageNumber) { NormalizeSettings(); double minimum = _minimumMultiplier.Value; double maximum = _maximumMultiplier.Value; double num = ClampMultiplier(_startingMultiplier.Value, minimum, maximum); int num2 = Math.Max(1, stageNumber); for (int i = 2; i <= num2; i++) { bool flag = (i - 1) % 5 == 0; num += (double)(flag ? _loopIncrement.Value : _stageIncrement.Value); num = ClampMultiplier(num, minimum, maximum); } return (float)num; } private static double ClampMultiplier(double value, double minimum, double maximum) { if (double.IsNaN(value)) { value = minimum; } return Math.Max(minimum, Math.Min(maximum, value)); } private void OnSettingChanged(object sender, EventArgs eventArgs) { NormalizeSettings(); } private void NormalizeSettings() { if (_normalizing) { return; } bool flag = false; _normalizing = true; try { if (IsInvalidNumber(_startingMultiplier.Value)) { _startingMultiplier.Value = 1f; flag = true; } if (IsInvalidNumber(_stageIncrement.Value)) { _stageIncrement.Value = 0f; flag = true; } if (IsInvalidNumber(_loopIncrement.Value)) { _loopIncrement.Value = 0f; flag = true; } float num = _minimumMultiplier.Value; if (IsInvalidNumber(num) || num < 0f) { num = 0f; _minimumMultiplier.Value = num; flag = true; } float value = _maximumMultiplier.Value; if (IsInvalidNumber(value) || value < num) { value = num; _maximumMultiplier.Value = value; flag = true; } } finally { _normalizing = false; } if (!flag) { return; } try { _config.Save(); } catch (Exception ex) { Plugin.ModLog.LogWarning((object)("Could not save normalized settings: " + ex.Message)); } } private static bool IsInvalidNumber(float value) { if (!float.IsNaN(value)) { return float.IsInfinity(value); } return true; } } }