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 BattlePotion v0.2.7
BattlePotion.dll
Decompiled 3 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BattlePotion.Logic; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.0.0.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 BattlePotion { internal class BattlePotionCommand : ConsoleCommand { public override string Name => "battlepotion"; public override string Help => "Show Battle Mead buff state and multipliers for the current biome"; public override void Run(string[] args, Terminal context) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null) { if (context != null) { context.AddString("No local player."); } return; } DateTime time = ZNet.instance.GetTime(); long num = BuffState.ReadUntil(localPlayer); double num2 = ((num > time.Ticks) ? TimeSpan.FromTicks(num - time.Ticks).TotalSeconds : 0.0); Biome currentBiome = localPlayer.GetCurrentBiome(); BiomeMultipliers biomeMultipliers = BattlePotionConfig.ForSpawner(currentBiome, currentBiome); if (context != null) { context.AddString((num2 > 0.0) ? $"Battle Fury active, {num2:0}s left" : "Battle Fury inactive"); } if (context != null) { context.AddString($"Biome {currentBiome}: frequency x{biomeMultipliers.Frequency}, chance x{biomeMultipliers.Chance}, cap x{biomeMultipliers.Cap}, level-up x{biomeMultipliers.Level}"); } if (!BattlePotionConfig.Enabled.Value && context != null) { context.AddString("Spawn boost is disabled in the config."); } } } internal static class BattlePotionConfig { private sealed class BiomeEntries { public ConfigEntry<float> Frequency; public ConfigEntry<float> Chance; public ConfigEntry<float> Cap; public ConfigEntry<float> Level; public BiomeMultipliers Value => new BiomeMultipliers(Frequency.Value, Chance.Value, Cap.Value, Level.Value); } private static readonly (Biome Biome, float Frequency, float Chance, float Cap, float Level)[] Defaults = new(Biome, float, float, float, float)[9] { ((Biome)1, 3f, 3f, 3f, 2f), ((Biome)8, 2.5f, 2.5f, 2.5f, 2f), ((Biome)2, 2f, 2f, 2f, 1.75f), ((Biome)4, 2f, 2f, 2f, 1.75f), ((Biome)16, 1.75f, 1.75f, 2f, 1.5f), ((Biome)512, 1.5f, 1.5f, 1.75f, 1.5f), ((Biome)32, 1.5f, 1.5f, 1.5f, 1.25f), ((Biome)64, 1.5f, 1.5f, 1.5f, 1.25f), ((Biome)256, 1f, 1f, 1f, 1f) }; private static readonly Dictionary<Biome, BiomeEntries> Biomes = new Dictionary<Biome, BiomeEntries>(); public static ConfigEntry<bool> Enabled; public static ConfigEntry<string> Recipe; public static ConfigEntry<string> CraftingStation; public static ConfigEntry<int> MinStationLevel; public static ConfigEntry<float> Duration; public static ConfigEntry<int> AbsoluteMaxPerSpawner; public static ConfigEntry<float> StackBonusPerExtraPlayer; public static ConfigEntry<int> GroupSizeBonus; public static ConfigEntry<bool> AffectRaids; public static ConfigEntry<float> LevelUpChanceCap; public static ConfigEntry<int> MaxLevelBonus; public static ConfigEntry<bool> DebugLogging; public static int Version { get; private set; } public static IEnumerable<Biome> ConfiguredBiomes => Biomes.Keys; public unsafe static void Bind(ConfigFile config) { //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) Enabled = config.Bind<bool>("0 - General", "Enabled", true, AdminOnly("Master switch for the spawn boost. The item stays craftable when disabled.")); Recipe = config.Bind<string>("1 - Item", "Recipe", "Wood:5", AdminOnly("Crafting cost as Item:Amount pairs separated by commas, e.g. Wood:5,Resin:2.")); CraftingStation = config.Bind<string>("1 - Item", "CraftingStation", "piece_workbench", AdminOnly("Prefab name of the crafting station (piece_workbench, forge, piece_cauldron, ...). Empty = craft by hand.")); MinStationLevel = config.Bind<int>("1 - Item", "MinStationLevel", 1, AdminOnly("Required crafting station level.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10))); Duration = config.Bind<float>("1 - Item", "Duration", 300f, AdminOnly("Buff duration in seconds. Applies to newly drunk meads.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 3600f))); AbsoluteMaxPerSpawner = config.Bind<int>("2 - Spawning", "AbsoluteMaxPerSpawner", 12, AdminOnly("Hard ceiling for any single spawner's max simultaneous creatures after scaling. Never lowers vanilla values.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100))); StackBonusPerExtraPlayer = config.Bind<float>("2 - Spawning", "StackBonusPerExtraPlayer", 0f, AdminOnly("Added to frequency/chance/cap multipliers per additional buffed player in the same zone. 0 = buffed players share one cap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f))); GroupSizeBonus = config.Bind<int>("2 - Spawning", "GroupSizeBonus", 0, AdminOnly("Extra creatures added to the maximum pack size of boosted spawners (still limited by the cap).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10))); AffectRaids = config.Bind<bool>("2 - Spawning", "AffectRaids", false, AdminOnly("Also boost raid/event spawns.")); LevelUpChanceCap = config.Bind<float>("3 - Stars", "LevelUpChanceCap", 50f, AdminOnly("Upper limit (percent) for the boosted star level-up chance, before the game's own world and location modifiers. Never lowers a spawner's vanilla chance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f))); MaxLevelBonus = config.Bind<int>("3 - Stars", "MaxLevelBonus", 0, AdminOnly("Extra star levels allowed above the spawner's vanilla maximum.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3))); (Biome, float, float, float, float)[] defaults = Defaults; for (int i = 0; i < defaults.Length; i++) { (Biome, float, float, float, float) tuple = defaults[i]; Biome item = tuple.Item1; float item2 = tuple.Item2; float item3 = tuple.Item3; float item4 = tuple.Item4; float item5 = tuple.Item5; string text = "4 - Biome " + ((object)(*(Biome*)(&item))/*cast due to .constrained prefix*/).ToString(); Biomes[item] = new BiomeEntries { Frequency = config.Bind<float>(text, "SpawnFrequencyMultiplier", item2, AdminOnly("Divides the spawn interval: 2 = spawn attempts twice as often. 1 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f))), Chance = config.Bind<float>(text, "SpawnChanceMultiplier", item3, AdminOnly("Multiplies each attempt's spawn chance (capped at 100%). 1 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f))), Cap = config.Bind<float>(text, "MaxSpawnedMultiplier", item4, AdminOnly("Multiplies each spawner's max simultaneous creatures. 1 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f))), Level = config.Bind<float>(text, "LevelUpChanceMultiplier", item5, AdminOnly("Multiplies the star level-up chance. 1 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f))) }; } DebugLogging = config.Bind<bool>("9 - Debug", "DebugLogging", false, "Log every scaled spawn list (local only)."); config.SettingChanged += delegate { Version++; }; } public static BiomeMultipliers ForSpawner(Biome spawnerBiomes, Biome zoneBiomes) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) BiomeMultipliers biomeMultipliers = BiomeMultipliers.None; foreach (KeyValuePair<Biome, BiomeEntries> biome in Biomes) { if ((spawnerBiomes & biome.Key) != 0 && (zoneBiomes & biome.Key) != 0) { biomeMultipliers = BiomeMultipliers.Max(biomeMultipliers, biome.Value.Value); } } return biomeMultipliers; } public static IEnumerable<(string Item, int Amount)> ParseRecipe(string recipe) { string[] array = recipe.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string[] array2 = text.Trim().Split(new char[1] { ':' }); if (array2.Length == 2 && int.TryParse(array2[1], out var result) && result > 0) { yield return (Item: array2[0].Trim(), Amount: result); } else { BattlePotionPlugin.Log.LogWarning((object)("Ignoring invalid recipe entry '" + text + "'")); } } } private static ConfigDescription AdminOnly(string description, AcceptableValueBase range = null) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return new ConfigDescription(description, range, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } }); } } [BepInPlugin("com.robert.battlepotion", "BattlePotion", "0.2.7")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class BattlePotionPlugin : BaseUnityPlugin { private const string ItemName = "BattleMead"; private const string BasePrefab = "MeadTasty"; internal static ManualLogSource Log; private Harmony _harmony; private SE_BattleFury _statusEffect; private void Awake() { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; BattlePotionConfig.Bind(((BaseUnityPlugin)this).Config); BattlePotionConfig.Duration.SettingChanged += delegate { ApplyDuration(); }; BattlePotionConfig.Recipe.SettingChanged += delegate { ApplyRecipe(); }; BattlePotionConfig.CraftingStation.SettingChanged += delegate { ApplyRecipe(); }; BattlePotionConfig.MinStationLevel.SettingChanged += delegate { ApplyRecipe(); }; AddLocalizations(); PrefabManager.OnVanillaPrefabsAvailable += AddItems; ItemManager.OnItemsRegistered += ApplyRecipe; CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BattlePotionCommand()); _harmony = new Harmony("com.robert.battlepotion"); _harmony.PatchAll(typeof(BattlePotionPlugin).Assembly); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void AddItems() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown try { _statusEffect = ScriptableObject.CreateInstance<SE_BattleFury>(); ((Object)_statusEffect).name = "SE_BattleFury"; ((StatusEffect)_statusEffect).m_name = "$se_battlefury"; ((StatusEffect)_statusEffect).m_category = "battlepotion"; ((StatusEffect)_statusEffect).m_tooltip = "$se_battlefury_tooltip"; ((StatusEffect)_statusEffect).m_startMessageType = (MessageType)2; ((StatusEffect)_statusEffect).m_startMessage = "$se_battlefury_start"; ((StatusEffect)_statusEffect).m_stopMessage = "$se_battlefury_stop"; ApplyDuration(); ItemConfig val = new ItemConfig { Name = "$item_battlemead", Description = "$item_battlemead_desc", CraftingStation = (string)((ConfigEntryBase)BattlePotionConfig.CraftingStation).DefaultValue, MinStationLevel = (int)((ConfigEntryBase)BattlePotionConfig.MinStationLevel).DefaultValue, Requirements = BattlePotionConfig.ParseRecipe((string)((ConfigEntryBase)BattlePotionConfig.Recipe).DefaultValue).Select<(string, int), RequirementConfig>((Func<(string, int), RequirementConfig>)(((string Item, int Amount) r) => new RequirementConfig(r.Item, r.Amount, 0, false))).ToArray() }; CustomItem val2 = new CustomItem("BattleMead", "MeadTasty", val); SharedData shared = val2.ItemDrop.m_itemData.m_shared; ((StatusEffect)_statusEffect).m_icon = ((shared.m_icons.Length != 0) ? shared.m_icons[0] : null); shared.m_consumeStatusEffect = (StatusEffect)(object)_statusEffect; ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)(object)_statusEffect, false)); ItemManager.Instance.AddItem(val2); Log.LogInfo((object)"Registered BattleMead"); } catch (Exception arg) { Log.LogError((object)string.Format("Failed to register {0}: {1}", "BattleMead", arg)); } finally { PrefabManager.OnVanillaPrefabsAvailable -= AddItems; } } private void ApplyDuration() { if ((Object)(object)_statusEffect != (Object)null) { ((StatusEffect)_statusEffect).m_ttl = BattlePotionConfig.Duration.Value; } } private static void ApplyRecipe() { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown if ((Object)(object)ObjectDB.instance == (Object)null) { return; } List<Recipe> list = ObjectDB.instance.m_recipes.Where((Recipe r) => (Object)(object)r != (Object)null && (Object)(object)r.m_item != (Object)null && ((Object)r.m_item).name == "BattleMead").ToList(); if (list.Count == 0) { return; } List<Requirement> list2 = new List<Requirement>(); foreach (var item3 in BattlePotionConfig.ParseRecipe(BattlePotionConfig.Recipe.Value)) { string item = item3.Item; int item2 = item3.Amount; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item); ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val == (Object)null) { Log.LogWarning((object)("Recipe item '" + item + "' not found, keeping the current BattleMead recipe")); return; } list2.Add(new Requirement { m_resItem = val, m_amount = item2, m_amountPerLevel = 0, m_recover = false }); } if (list2.Count == 0) { Log.LogWarning((object)("Recipe '" + BattlePotionConfig.Recipe.Value + "' has no valid entries, keeping the current BattleMead recipe")); return; } string text = BattlePotionConfig.CraftingStation.Value?.Trim(); CraftingStation val2 = null; if (!string.IsNullOrEmpty(text)) { val2 = Cache.GetPrefab<CraftingStation>(text); if ((Object)(object)val2 == (Object)null) { Log.LogWarning((object)("Crafting station '" + text + "' not found, keeping the current BattleMead recipe")); return; } } foreach (Recipe item4 in list) { item4.m_resources = list2.ToArray(); item4.m_craftingStation = val2; item4.m_minStationLevel = BattlePotionConfig.MinStationLevel.Value; } Log.LogInfo((object)string.Format("Applied {0} recipe: {1} at {2} lvl {3}", "BattleMead", BattlePotionConfig.Recipe.Value, ((val2 != null) ? ((Object)val2).name : null) ?? "hand", BattlePotionConfig.MinStationLevel.Value)); } private static void AddLocalizations() { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "English"; localization.AddTranslation(ref text, new Dictionary<string, string> { { "item_battlemead", "Battle Mead" }, { "item_battlemead_desc", "A bitter brew that calls out to every creature nearby. Monsters come faster, in greater numbers and stronger." }, { "se_battlefury", "Battle Fury" }, { "se_battlefury_tooltip", "Nearby enemies spawn faster, in greater numbers and with a higher chance of stars. Strength depends on the biome." }, { "se_battlefury_start", "The wilds answer your challenge" }, { "se_battlefury_stop", "The battle fury fades" } }); } } internal static class BuffState { private static readonly int UntilHash = StringExtensionMethods.GetStableHashCode("BattlePotion_until"); public static void Write(Character character, long untilTicks) { ZNetView val = (Object.op_Implicit((Object)(object)character) ? ((Component)character).GetComponent<ZNetView>() : null); if ((Object)(object)val != (Object)null && val.IsValid() && val.IsOwner()) { val.GetZDO().Set(UntilHash, untilTicks); } } public static long ReadUntil(Player player) { ZNetView val = (Object.op_Implicit((Object)(object)player) ? ((Component)player).GetComponent<ZNetView>() : null); if (!((Object)(object)val != (Object)null) || !val.IsValid()) { return 0L; } return val.GetZDO().GetLong(UntilHash, 0L); } public static bool IsActive(Player player, long nowTicks) { return ReadUntil(player) > nowTicks; } } public class SE_BattleFury : StatusEffect { private const float SyncInterval = 2f; private const float ExpiryGrace = 3f; private float m_syncTimer; public override void Setup(Character character) { ((StatusEffect)this).Setup(character); Sync(); } public override void UpdateStatusEffect(float dt) { ((StatusEffect)this).UpdateStatusEffect(dt); m_syncTimer += dt; if (m_syncTimer >= 2f) { m_syncTimer = 0f; Sync(); } } public override void ResetTime() { ((StatusEffect)this).ResetTime(); Sync(); } public override void Stop() { ((StatusEffect)this).Stop(); if (Object.op_Implicit((Object)(object)base.m_character)) { BuffState.Write(base.m_character, 0L); } } private void Sync() { if (Object.op_Implicit((Object)(object)base.m_character) && !((Object)(object)ZNet.instance == (Object)null)) { double value = base.m_ttl - base.m_time + 3f; BuffState.Write(base.m_character, ZNet.instance.GetTime().AddSeconds(value).Ticks); } } } [HarmonyPatch(typeof(SpawnSystem), "UpdateSpawnList")] internal static class SpawnSystemPatch { private static readonly FieldRef<SpawnSystem, Heightmap> HeightmapRef = AccessTools.FieldRefAccess<SpawnSystem, Heightmap>("m_heightmap"); private static readonly List<Player> NearPlayers = AccessTools.StaticFieldRefAccess<SpawnSystem, List<Player>>("m_tempNearPlayers"); private const float CacheLifetime = 60f; private static readonly Dictionary<(List<SpawnData> Source, int Signature), List<SpawnData>> Cache = new Dictionary<(List<SpawnData>, int), List<SpawnData>>(); private static int s_cacheVersion = -1; private static float s_cacheBuiltAt; private static void Prefix(SpawnSystem __instance, ref List<SpawnData> spawners, bool eventSpawners) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected I4, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if (!BattlePotionConfig.Enabled.Value || spawners == null || spawners.Count == 0 || (Object)(object)ZNet.instance == (Object)null || (eventSpawners && !BattlePotionConfig.AffectRaids.Value)) { return; } int num = CountBuffedPlayers(); if (num == 0) { return; } Heightmap val = HeightmapRef.Invoke(__instance); if ((Object)(object)val == (Object)null) { return; } Biome val2 = (Biome)0; foreach (Biome configuredBiome in BattlePotionConfig.ConfiguredBiomes) { if (val.HaveBiome(configuredBiome)) { val2 |= configuredBiome; } } InvalidateCacheIfStale(); int item = (val2 * 397) ^ num; (List<SpawnData>, int) key = (spawners, item); if (!Cache.TryGetValue(key, out var value)) { value = BuildScaledList(spawners, val2, num); Cache[key] = value; } spawners = value; } private static int CountBuffedPlayers() { long ticks = ZNet.instance.GetTime().Ticks; int num = 0; foreach (Player nearPlayer in NearPlayers) { if (BuffState.IsActive(nearPlayer, ticks)) { num++; } } return num; } private static void InvalidateCacheIfStale() { if (s_cacheVersion != BattlePotionConfig.Version || Time.time - s_cacheBuiltAt > 60f) { Cache.Clear(); s_cacheVersion = BattlePotionConfig.Version; s_cacheBuiltAt = Time.time; } } private static List<SpawnData> BuildScaledList(List<SpawnData> source, Biome zoneBiomes, int buffed) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) List<SpawnData> list = new List<SpawnData>(source.Count); int value = BattlePotionConfig.AbsoluteMaxPerSpawner.Value; float value2 = BattlePotionConfig.StackBonusPerExtraPlayer.Value; float value3 = BattlePotionConfig.LevelUpChanceCap.Value; int value4 = BattlePotionConfig.MaxLevelBonus.Value; int value5 = BattlePotionConfig.GroupSizeBonus.Value; int num = 0; foreach (SpawnData item in source) { BiomeMultipliers biomeMultipliers = SpawnScaling.Combine(BattlePotionConfig.ForSpawner(item.m_biome, zoneBiomes), buffed, value2); if (biomeMultipliers.IsNeutral) { list.Add(item); continue; } SpawnData val = item.Clone(); val.m_spawnInterval = SpawnScaling.Interval(item.m_spawnInterval, biomeMultipliers.Frequency); val.m_spawnChance = SpawnScaling.Chance(item.m_spawnChance, biomeMultipliers.Chance); val.m_groupSizeMax = SpawnScaling.GroupSizeMax(item.m_groupSizeMax, value5); val.m_maxSpawned = SpawnScaling.MaxSpawned(item.m_maxSpawned, biomeMultipliers.Cap, value); val.m_overrideLevelupChance = SpawnScaling.LevelUpChance(item.m_overrideLevelupChance, biomeMultipliers.Level, value3); val.m_maxLevel = SpawnScaling.MaxLevel(item.m_maxLevel, value4); list.Add(val); num++; } if (BattlePotionConfig.DebugLogging.Value) { BattlePotionPlugin.Log.LogInfo((object)$"Scaled {num}/{source.Count} spawners for biomes [{zoneBiomes}] with {buffed} buffed player(s)"); } return list; } } internal static class ModInfo { public const string Guid = "com.robert.battlepotion"; public const string Name = "BattlePotion"; public const string Version = "0.2.7"; } } namespace BattlePotion.Logic { public readonly struct BiomeMultipliers { public static readonly BiomeMultipliers None = new BiomeMultipliers(1f, 1f, 1f, 1f); public readonly float Frequency; public readonly float Chance; public readonly float Cap; public readonly float Level; public bool IsNeutral { get { if (Frequency <= 1f && Chance <= 1f && Cap <= 1f) { return Level <= 1f; } return false; } } public BiomeMultipliers(float frequency, float chance, float cap, float level) { Frequency = frequency; Chance = chance; Cap = cap; Level = level; } public static BiomeMultipliers Max(BiomeMultipliers a, BiomeMultipliers b) { return new BiomeMultipliers(Math.Max(a.Frequency, b.Frequency), Math.Max(a.Chance, b.Chance), Math.Max(a.Cap, b.Cap), Math.Max(a.Level, b.Level)); } } public static class SpawnScaling { public const float VanillaLevelUpChance = 10f; public static BiomeMultipliers Combine(BiomeMultipliers biome, int buffedPlayers, float stackBonusPerExtraPlayer) { if (buffedPlayers <= 0) { return BiomeMultipliers.None; } float num = Math.Max(0f, stackBonusPerExtraPlayer) * (float)(buffedPlayers - 1); return new BiomeMultipliers(biome.Frequency + num, biome.Chance + num, biome.Cap + num, biome.Level); } public static float Interval(float interval, float frequency) { if (!(frequency > 1f)) { return interval; } return interval / frequency; } public static float Chance(float chance, float multiplier) { if (!(multiplier > 1f)) { return chance; } return Math.Min(100f, chance * multiplier); } public static int MaxSpawned(int maxSpawned, float cap, int ceiling) { if (maxSpawned <= 0 || cap <= 1f) { return maxSpawned; } int val = (int)Math.Ceiling((float)maxSpawned * cap); return Math.Max(maxSpawned, Math.Min(val, ceiling)); } public static float LevelUpChance(float overrideChance, float level, float capPercent) { if (level <= 1f) { return overrideChance; } float num = ((overrideChance > 0f) ? overrideChance : 10f); return Math.Max(num, Math.Min(capPercent, num * level)); } public static int MaxLevel(int maxLevel, int bonus) { if (maxLevel <= 1 || bonus <= 0) { return maxLevel; } return maxLevel + bonus; } public static int GroupSizeMax(int groupSizeMax, int bonus) { if (bonus <= 0) { return groupSizeMax; } return groupSizeMax + bonus; } } }