Some mods may be broken due to the recent Alloyed Collective update.
Decompiled source of PlayerCountConfig v1.2.1
PlayerCountConfig.dll
Decompiled 4 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."); } } }