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 PlayerCountConfig v1.2.1
PlayerCountConfig.dll
Decompiled 9 months agousing 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 MonoMod.RuntimeDetour; using On.RoR2; using On.RoR2.Artifacts; using R2API.Utils; using RoR2; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("PlayerCountConfig")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("PlayerCountConfig")] [assembly: AssemblyTitle("PlayerCountConfig")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace ShrineClamper.Utils { public static class ListHandlers { public static List<ItemDef> ItemDefList { get; } } } namespace PlayerCountConfig { [BepInDependency("com.bepis.r2api", "3.0.7")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.thebugreport.playerCountConfig", "Player Count Config", "1.0.0")] public class Main : BaseUnityPlugin { public const string ModGUID = "com.thebugreport.playerCountConfig"; public const string ModName = "Player Count Config"; public const string ModVersion = "1.0.0"; private Hook participatingPlayerCountHook; private Hook livingPlayerCountHook; private Hook getDifficultyScaledCostHook; private Hook getDifficultyScaledCostWithCoeffHook; private int shrineStacks; private int bossItems = 1; private BossGroup teleporterGroup; private int sacrificeOffset = 1; private static readonly List<string> interactableFreeze = new List<string> { "MAP_BAZAAR_TITLE", "MAP_ARENA_TITLE", "MAP_LIMBO_TITLE", "MAP_MYSTERYSPACE_TITLE" }; public void Awake() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown PlayerConfigs.Init(((BaseUnityPlugin)this).Config); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded Point 1"); SacrificeArtifactManager.OnPrePopulateSceneServer += (hook_OnPrePopulateSceneServer)delegate(orig_OnPrePopulateSceneServer orig, SceneDirector self) { sacrificeOffset = 2; orig.Invoke(self); }; SceneDirector.PlaceTeleporter += (hook_PlaceTeleporter)delegate(orig_PlaceTeleporter orig, SceneDirector self) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(self); int num5 = 200; ClassicStageInfo component = ((Component)SceneInfo.instance).GetComponent<ClassicStageInfo>(); if (Object.op_Implicit((Object)(object)component)) { num5 = component.sceneDirectorInteractibleCredits; int participatingPlayerCount = Run.instance.participatingPlayerCount; float num6 = (float)(0.95 + (double)participatingPlayerCount * 0.05); num6 *= (float)Math.Max(1.0 + 0.1 * (double)Math.Min(participatingPlayerCount * 2 - Run.instance.stageClearCount - 2, 3), 1.0); num5 = (int)((float)num5 / num6); if (component.bonusInteractibleCreditObjects != null) { BonusInteractibleCreditObject[] bonusInteractibleCreditObjects = component.bonusInteractibleCreditObjects; foreach (BonusInteractibleCreditObject val in bonusInteractibleCreditObjects) { if (val.objectThatGrantsPointsIfEnabled.activeSelf) { num5 += val.points / participatingPlayerCount; } } } } if (PlayerConfigs.OverrideInteractableCount.Value && (!Object.op_Implicit((Object)(object)SceneInfo.instance) || !interactableFreeze.Contains(SceneInfo.instance.sceneDef.nameToken))) { int num7 = (PlayerConfigs.UseInteractableScaling.Value ? (Run.instance.participatingPlayerCount * PlayerConfigs.FixedOrMultiplierInteractableBase.Value) : PlayerConfigs.FixedOrMultiplierInteractableBase.Value) + PlayerConfigs.InteractableCountOffset.Value; int num8 = (int)Math.Floor((float)num5 * ((float)(1 + num7) / 2f)) + PlayerConfigs.InteractableCreditsOffset.Value; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Setting interactableCredit from {self.interactableCredit} / {num5} to {num8}"); self.interactableCredit = num8; } sacrificeOffset = 1; if (PlayerConfigs.OverrideMonsterCount.Value) { int monsterCredit = self.monsterCredit; int num9 = (PlayerConfigs.UseMonsterScaling.Value ? (Run.instance.participatingPlayerCount * PlayerConfigs.FixedOrMultiplierMonsterBase.Value) : PlayerConfigs.FixedOrMultiplierMonsterBase.Value) + PlayerConfigs.MonsterCountOffset.Value; int num10 = (int)Math.Floor((float)monsterCredit * ((float)(1 + num9) / 2f)) + PlayerConfigs.MonsterCreditsOffset.Value; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Setting monsterCredit from {self.monsterCredit} to {num10}"); self.monsterCredit = num10; } }; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded Point 2"); TeleporterInteraction.AddShrineStack += (hook_AddShrineStack)delegate(orig_AddShrineStack orig, TeleporterInteraction self) { shrineStacks++; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[ShrineClamper] Shrine activated. Total shrine stacks: {shrineStacks}"); orig.Invoke(self); }; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded Point 3"); TeleporterInteraction.OnInteractionBegin += (hook_OnInteractionBegin)delegate(orig_OnInteractionBegin orig, TeleporterInteraction self, Interactor user) { bossItems = (PlayerConfigs.OverrideBaseCount.Value ? (PlayerConfigs.FixedOrMultiplierBaseTeleporter.Value + PlayerConfigs.PlayerCountOffsetTeleporter.Value) : Run.instance.participatingPlayerCount); orig.Invoke(self, user); }; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded Point 4"); TeleporterInteraction.OnBossDirectorSpawnedMonsterServer += (hook_OnBossDirectorSpawnedMonsterServer)delegate(orig_OnBossDirectorSpawnedMonsterServer orig, TeleporterInteraction self, GameObject monster) { if ((Object)(object)teleporterGroup == (Object)null) { teleporterGroup = self.bossGroup; } }; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded Point 5"); BossGroup.DropRewards += (hook_DropRewards)delegate(orig_DropRewards orig, BossGroup self) { Run instance = Run.instance; int num = ((instance == null) ? 1 : instance.participatingPlayerCount); int num2 = ((!PlayerConfigs.OverrideBaseCount.Value) ? num : ((!PlayerConfigs.UsePlayerCountScaling.Value) ? (PlayerConfigs.FixedOrMultiplierBaseTeleporter.Value + PlayerConfigs.PlayerCountOffsetTeleporter.Value) : (num * PlayerConfigs.FixedOrMultiplierBaseTeleporter.Value + PlayerConfigs.PlayerCountOffsetTeleporter.Value))); int num3 = 0; if ((Object)(object)teleporterGroup == (Object)(object)self) { num3 = ((!PlayerConfigs.OverrideShrines.Value) ? (shrineStacks * num) : ((!PlayerConfigs.UsePlayerCountScalingShrine.Value) ? (shrineStacks * (PlayerConfigs.FixedOrMultiplierShrine.Value + PlayerConfigs.ShrineCountOffset.Value)) : (shrineStacks * (PlayerConfigs.FixedOrMultiplierShrine.Value * num + PlayerConfigs.ShrineCountOffset.Value)))); } int num4 = num2 + num3 - 1; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Total drops calculated: Base Drops: {num2}, Shrine Bonus: {num3}, Total Drops: {num4 + 1}"); self.scaleRewardsByPlayerCount = false; self.bonusRewardCount = num4; orig.Invoke(self); if ((Object)(object)teleporterGroup == (Object)(object)self) { shrineStacks = 0; } }; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded Point 6"); Stage.BeginServer += (hook_BeginServer)delegate(orig_BeginServer orig, Stage self) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Stage started — resetting shrineStacks to 0."); shrineStacks = 0; orig.Invoke(self); }; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded Point 7"); } private static int GetEffectivePricePlayerCount() { if (!PlayerConfigs.UseInteractablePriceScaling.Value) { return PlayerConfigs.FixedOrMultiplierInteractablePriceBase.Value; } return Run.instance.participatingPlayerCount * PlayerConfigs.FixedOrMultiplierInteractablePriceBase.Value; } private int CustomGetDifficultyScaledCost(Run self, int baseCost) { if (PlayerConfigs.OverrideInteractablePrice.Value) { return CalculateScaledPrice(baseCost, GetEffectivePricePlayerCount()); } return getDifficultyScaledCostHook.GenerateTrampoline<Func<Run, int, int>>()(self, baseCost); } private int CustomGetDifficultyScaledCostWithCoeff(Run self, int baseCost, float difficultyCoefficient) { if (PlayerConfigs.OverrideInteractablePrice.Value) { return CalculateScaledPrice(baseCost, GetEffectivePricePlayerCount()); } return getDifficultyScaledCostWithCoeffHook.GenerateTrampoline<Func<Run, int, float, int>>()(self, baseCost, difficultyCoefficient); } private static int CalculateScaledPrice(int originalCost, int targetPlayerCount) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) float runStopwatch = Run.instance.GetRunStopwatch(); DifficultyDef difficultyDef = DifficultyCatalog.GetDifficultyDef(Run.instance.selectedDifficulty); float num = Mathf.Floor(runStopwatch * (1f / 60f)); float num2 = (float)(targetPlayerCount + PlayerConfigs.InteractablePriceOffset.Value) * 0.3f; float num3 = 0.7f + num2; float num4 = Mathf.Pow((float)targetPlayerCount, 0.2f); float num5 = 0.0506f * difficultyDef.scalingValue * num4; float num6 = Mathf.Pow(1.15f, (float)Run.instance.stageClearCount); float num7 = (num3 + num5 * num) * num6; int num8 = Mathf.FloorToInt((float)((double)originalCost * Math.Pow(num7, 1.25))); int num9 = Mathf.Max(0, num8); num9 += PlayerConfigs.InteractableCreditsPriceOffset.Value; Debug.Log((object)$"[PlayerCountConfig] Changing interactable price... Base cost: {originalCost}, Target player count: {targetPlayerCount}, Final price: {num9}"); return num9; } private static int OverrideParticipatingPlayerCount(Func<Run, int> orig, Run self) { if (!PlayerConfigs.OverridePlayerCount.Value) { return orig(self); } int num = (PlayerConfigs.UsePlayerScaling.Value ? (orig(self) * PlayerConfigs.FixedOrMultiplierBase.Value) : PlayerConfigs.FixedOrMultiplierBase.Value) + PlayerConfigs.PlayerCountOffset.Value; return Mathf.Max(PlayerConfigs.MinimumPlayerCount.Value, num); } public void OnEnable() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Expected O, but got Unknown ((BaseUnityPlugin)this).Logger.LogInfo((object)"Applying player count hooks..."); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; MethodInfo methodInfo = typeof(Run).GetProperty("participatingPlayerCount", bindingAttr)?.GetGetMethod(nonPublic: true); typeof(Run).GetProperty("livingPlayerCount", bindingAttr)?.GetGetMethod(nonPublic: true); if (methodInfo != null) { participatingPlayerCountHook = new Hook((MethodBase)methodInfo, (Delegate)new Func<Func<Run, int>, Run, int>(OverrideParticipatingPlayerCount)); participatingPlayerCountHook.Apply(); } MethodInfo method = typeof(Run).GetMethod("GetDifficultyScaledCost", bindingAttr, null, new Type[1] { typeof(int) }, null); if (method != null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HOOK] Found GetDifficultyScaledCost method."); getDifficultyScaledCostHook = new Hook((MethodBase)method, (Delegate)new Func<Run, int, int>(CustomGetDifficultyScaledCost)); getDifficultyScaledCostHook.Apply(); } MethodInfo method2 = typeof(Run).GetMethod("GetDifficultyScaledCost", bindingAttr, null, new Type[2] { typeof(int), typeof(float) }, null); if (method2 != null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HOOK] Found GetDifficultyScaledCost with coeff method."); getDifficultyScaledCostWithCoeffHook = new Hook((MethodBase)method2, (Delegate)new Func<Run, int, float, int>(CustomGetDifficultyScaledCostWithCoeff)); getDifficultyScaledCostWithCoeffHook.Apply(); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Applied hooks!"); } public void OnDisable() { Hook obj = participatingPlayerCountHook; if (obj != null) { obj.Undo(); } Hook obj2 = livingPlayerCountHook; if (obj2 != null) { obj2.Undo(); } Hook obj3 = getDifficultyScaledCostHook; if (obj3 != null) { obj3.Undo(); } Hook obj4 = getDifficultyScaledCostWithCoeffHook; if (obj4 != null) { obj4.Undo(); } } } public static class PlayerConfigs { public static ConfigEntry<bool> OverridePlayerCount { get; set; } public static ConfigEntry<bool> UsePlayerScaling { get; set; } public static ConfigEntry<int> FixedOrMultiplierBase { get; set; } public static ConfigEntry<int> PlayerCountOffset { get; set; } public static ConfigEntry<int> MinimumPlayerCount { get; set; } public static ConfigEntry<bool> OverrideInteractableCount { get; set; } public static ConfigEntry<bool> UseInteractableScaling { get; set; } public static ConfigEntry<int> FixedOrMultiplierInteractableBase { get; set; } public static ConfigEntry<int> InteractableCountOffset { get; set; } public static ConfigEntry<int> InteractableCreditsOffset { get; set; } public static ConfigEntry<bool> OverrideMonsterCount { get; set; } public static ConfigEntry<bool> UseMonsterScaling { get; set; } public static ConfigEntry<int> FixedOrMultiplierMonsterBase { get; set; } public static ConfigEntry<int> MonsterCountOffset { get; set; } public static ConfigEntry<int> MonsterCreditsOffset { get; set; } public static ConfigEntry<bool> OverrideInteractablePrice { get; set; } public static ConfigEntry<bool> UseInteractablePriceScaling { get; set; } public static ConfigEntry<int> FixedOrMultiplierInteractablePriceBase { get; set; } public static ConfigEntry<int> InteractablePriceOffset { get; set; } public static ConfigEntry<int> InteractableCreditsPriceOffset { get; set; } public static ConfigEntry<bool> OverrideBaseCount { get; set; } public static ConfigEntry<bool> UsePlayerCountScaling { get; set; } public static ConfigEntry<int> FixedOrMultiplierBaseTeleporter { get; set; } public static ConfigEntry<int> PlayerCountOffsetTeleporter { get; set; } public static ConfigEntry<bool> OverrideShrines { get; set; } public static ConfigEntry<bool> UsePlayerCountScalingShrine { get; set; } public static ConfigEntry<int> FixedOrMultiplierShrine { get; set; } public static ConfigEntry<int> ShrineCountOffset { get; set; } public static void Init(ConfigFile config) { OverridePlayerCount = config.Bind<bool>("Player Count", "Override Player Count", false, "If enabled, replaces the game's player count logic with the custom settings below."); UsePlayerScaling = config.Bind<bool>("Player Count", "Scale By Actual Player Count", true, "If enabled, multiplies the actual player count by the multiplier below. If disabled, uses the multiplier as a fixed player count."); FixedOrMultiplierBase = config.Bind<int>("Player Count", "Player Count Multiplier", 1, "If scaling is enabled, multiplies the player count by this value. If disabled, sets the player count to this value."); PlayerCountOffset = config.Bind<int>("Player Count", "Player Count Offset", 0, "Adds or subtracts from the final player count after all other calculations."); MinimumPlayerCount = config.Bind<int>("Player Count", "Minimum Player Count", 1, "Ensures the player count never goes below this value."); OverrideInteractableCount = config.Bind<bool>("Interactable Credits", "Override Interactable Credits", false, "If enabled, replaces the game's player count for interactable spawning specifically with the settings below."); UseInteractableScaling = config.Bind<bool>("Interactable Credits", "Scale Interactables By Player Count", true, "If enabled, multiplies the player count by the multiplier below for interactable credits. If disabled, uses the multiplier as a fixed player count. Each player adds 50% more credits after the first. (0 players halfs the credits. -1 sets the credits to 0)."); FixedOrMultiplierInteractableBase = config.Bind<int>("Interactable Credits", "Player Count Multiplier", 1, "If scaling is enabled, multiplies the player count by this value for interactable credits. If disabled, sets the player count to this value."); InteractableCountOffset = config.Bind<int>("Interactable Credits", "Player Count Offset", 0, "Adds or subtracts from the final player count after all other calculations."); InteractableCreditsOffset = config.Bind<int>("Interactable Credits", "Interactable Credits Offset", 0, "Adds or subtracts a flat number of interactable credits after all other calculations."); OverrideInteractablePrice = config.Bind<bool>("Interactable Price", "Override Interactable Price", false, "If enabled, replaces the game's interactable price logic with the custom settings below."); UseInteractablePriceScaling = config.Bind<bool>("Interactable Price", "Scale Prices By Player Count", true, "If enabled, multiplies the player count by the multiplier below for price scaling. If disabled, uses the multiplier as a fixed player count for prices."); FixedOrMultiplierInteractablePriceBase = config.Bind<int>("Interactable Price", "Player Count Multiplier", 1, "If scaling is enabled, multiplies the player count by this value for prices. If disabled, sets the player count to this value."); InteractablePriceOffset = config.Bind<int>("Interactable Price", "Player Count Offset", 0, "Adds or subtracts from the player count after all other calculations."); InteractableCreditsPriceOffset = config.Bind<int>("Interactable Price", "Flat Price Offset", 0, "Adds or subtracts a flat amount to the final interactable price after all other calculations."); OverrideMonsterCount = config.Bind<bool>("Monster Credits", "Override Monster Spawn Credits", false, "If enabled, replaces the game's monster spawn logic (specifically when starting a stage) with the custom settings below."); UseMonsterScaling = config.Bind<bool>("Monster Credits", "Scale Monster Credits By Player Count", false, "If enabled, multiplies the player count by the multiplier below for monster credits. If disabled, uses the multiplier as a fixed player count. Each player adds 50% more credits after the first. (0 players halfs the credits. -1 sets the credits to 0)."); FixedOrMultiplierMonsterBase = config.Bind<int>("Monster Credits", "Player Count Multiplier", 1, "If scaling is enabled, multiplies the player count by this value for monster credits. If disabled, sets the player count to this value."); MonsterCountOffset = config.Bind<int>("Monster Credits", "Player Count Offset", 0, "Adds or subtracts from the final player count after all other calculations."); MonsterCreditsOffset = config.Bind<int>("Monster Credits", "Monster Credits Offset", 0, "Adds or subtracts a flat number of monster credits after all other calculations."); OverrideBaseCount = config.Bind<bool>("Boss Drops", "Override Base Rewards", false, "If true, overrides the base number of items dropped by the teleporter before Shrine of the Mountain bonuses."); UsePlayerCountScaling = config.Bind<bool>("Boss Drops", "Use Player Scaling", true, "If true, base item drops scale with player count. If false, uses the fixed value below."); FixedOrMultiplierBaseTeleporter = config.Bind<int>("Boss Drops", "Player Count Multiplier", 1, "If scaling is enabled, multiplies the player count by this value for base drops. If disabled, sets the base drop count."); PlayerCountOffsetTeleporter = config.Bind<int>("Boss Drops", "Item Reward Offset", 0, "Adds or subtracts from the final base item count after all other calculations."); OverrideShrines = config.Bind<bool>("Mountain Shrines", "Override Shrine Rewards", false, "If true, overrides how Shrine of the Mountain affects teleporter item drops."); UsePlayerCountScalingShrine = config.Bind<bool>("Mountain Shrines", "Use Player Scaling", true, "If true, shrine bonuses scale with player count. If false, uses the fixed value below."); FixedOrMultiplierShrine = config.Bind<int>("Mountain Shrines", "Multiplier", 1, "If scaling is enabled, multiplies shrine rewards by this value per player. If disabled, sets the shrine reward count per shrine."); ShrineCountOffset = config.Bind<int>("Mountain Shrines", "Shrine Reward Offset", 0, "Adds or subtracts from the shrine reward count after all other calculations per shrine."); } } }