Decompiled source of BattlePotion v0.2.7

BattlePotion.dll

Decompiled 3 days ago
using 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;
		}
	}
}