Decompiled source of Behemoths v1.0.0

Behemoths.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using Behemoths.Configuration;
using Behemoths.Patches;
using Behemoths.Services;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using ScalerCore;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("Vippy")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+53b7c28de9b66c8d70e13cb917c1c900e3037d6a")]
[assembly: AssemblyProduct("Behemoths")]
[assembly: AssemblyTitle("Behemoths")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 Behemoths
{
	[BepInPlugin("Vippy.Behemoths", "Behemoths", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Logger => Instance.BaseLogger;

		private ManualLogSource BaseLogger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_006a: Expected O, but got Unknown
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			PluginConfig.Init(((BaseUnityPlugin)this).Config);
			((Component)this).gameObject.AddComponent<LiveTuning>().Setup(((BaseUnityPlugin)this).Config);
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} loaded. LogLevel={PluginConfig.LoggingLevel.Value}");
		}

		internal static void LogAlways(string msg)
		{
			Logger.LogInfo((object)msg);
		}

		internal static void LogInfo(string msg)
		{
			if (PluginConfig.LoggingLevel.Value >= VerbosityLevel.Debug)
			{
				Logger.LogInfo((object)msg);
			}
		}

		internal static void LogVerbose(string msg)
		{
			if (PluginConfig.LoggingLevel.Value >= VerbosityLevel.Verbose)
			{
				Logger.LogDebug((object)msg);
			}
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "1.0.0";
	}
}
namespace Behemoths.Services
{
	internal class BehemothTremor : MonoBehaviour
	{
		private const float Interval = 0.45f;

		private const float MinSpeed = 1f;

		private const float NearDistance = 3f;

		private const float FarDistance = 14f;

		private const float Duration = 0.3f;

		private EnemyParent? _parent;

		private Rigidbody? _rb;

		private float _timer;

		public void Setup(EnemyParent parent, Rigidbody rb)
		{
			_parent = parent;
			_rb = rb;
		}

		private void Update()
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_parent == (Object)null || (Object)(object)_rb == (Object)null || !_parent.Spawned)
			{
				return;
			}
			_timer -= Time.deltaTime;
			if (_timer > 0f)
			{
				return;
			}
			_timer = 0.45f;
			if (!PluginConfig.BossTremors.Value)
			{
				return;
			}
			Vector3 velocity = _rb.velocity;
			if (!(((Vector3)(ref velocity)).sqrMagnitude < 1f))
			{
				GameDirector instance = GameDirector.instance;
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.CameraShake == (Object)null))
				{
					float num = 1.2f * PluginConfig.BossSizeMultiplier.Value;
					instance.CameraShake.ShakeDistance(num, 3f, 14f, _rb.position, 0.3f);
				}
			}
		}
	}
	internal static class BossAnnouncer
	{
		public static bool Show(MoonUI mui)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Expected O, but got Unknown
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Expected O, but got Unknown
			RunManager instance = RunManager.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)mui.textTitle == (Object)null)
			{
				return false;
			}
			int num = Mathf.Clamp(instance.moonLevel, 0, instance.moons.Count);
			SetGraphic(mui.moonGraphicPreviousest, instance.MoonGetIcon(num - 2));
			SetGraphic(mui.moonGraphicPrevious, instance.MoonGetIcon(num - 1));
			SetGraphic(mui.moonGraphicCurrent, instance.MoonGetIcon(num));
			SetGraphic(mui.moonGraphicNext, instance.MoonGetIcon(num + 1));
			float value = PluginConfig.BossSizeMultiplier.Value;
			float value2 = PluginConfig.BossHealthMultiplier.Value;
			float value3 = PluginConfig.BossDamageMultiplier.Value;
			float value4 = PluginConfig.OrbValueMultiplier.Value;
			float value5 = PluginConfig.ValuableValueMultiplier.Value;
			List<MoonAttribute> list = new List<MoonAttribute>
			{
				new MoonAttribute
				{
					text = ((value > 1.01f) ? $"Behemoths roam this level, {value:0.#}x their size" : "Behemoths roam this level")
				},
				new MoonAttribute
				{
					text = $"{value2:0.#}x the health, hits {value3:0.#}x as hard"
				},
				new MoonAttribute
				{
					text = $"Their orbs are worth {value4:0.#}x"
				}
			};
			if (value5 > 1.01f)
			{
				list.Add(new MoonAttribute
				{
					text = $"Every valuable is worth {value5:0.#}x more"
				});
			}
			((TMP_Text)mui.textTitle).text = "BOSS LEVEL";
			mui.attributes = list;
			mui.SetState((State)1);
			instance.moonLevelChanged = false;
			Plugin.LogAlways("[Announce] BOSS LEVEL popup shown");
			return true;
		}

		private static void SetGraphic(RawImage img, Texture tex)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)img == (Object)null))
			{
				img.texture = tex;
				((Graphic)img).color = (((Object)(object)tex != (Object)null) ? Color.white : Color.clear);
			}
		}
	}
	internal static class BossOdds
	{
		public static float ChanceFor(Level level)
		{
			float num = MapWeight(level);
			if (num <= 0f)
			{
				return 0f;
			}
			float value = PluginConfig.BaseChance.Value;
			float num2 = Mathf.Max(value, PluginConfig.MaxChance.Value);
			float num3 = Mathf.Lerp(value, num2, SemiFunc.RunGetDifficultyMultiplier1());
			if (num3 >= 100f)
			{
				return 100f;
			}
			return Mathf.Clamp(num3 * num, 0f, 100f);
		}

		public static float MapWeight(Level level)
		{
			string name = (((Object)(object)level != (Object)null) ? ((Object)level).name : string.Empty);
			if (Has(name, "Manor"))
			{
				return PluginConfig.MapManor.Value;
			}
			if (Has(name, "Arctic"))
			{
				return PluginConfig.MapArctic.Value;
			}
			if (Has(name, "Wizard"))
			{
				return PluginConfig.MapWizard.Value;
			}
			if (Has(name, "Museum"))
			{
				return PluginConfig.MapMuseum.Value;
			}
			return PluginConfig.MapOther.Value;
		}

		public static string MapName(Level level)
		{
			if ((Object)(object)level == (Object)null)
			{
				return "unknown map";
			}
			string name = ((Object)level).name;
			if (!name.StartsWith("Level - ", StringComparison.Ordinal))
			{
				return name;
			}
			return name.Substring("Level - ".Length);
		}

		private static bool Has(string name, string token)
		{
			return name.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0;
		}
	}
	internal static class BossRoundService
	{
		private const string LastBossStat = "behemothsLastBossLevel";

		private static readonly HashSet<Enemy> _bosses = new HashSet<Enemy>();

		private static readonly HashSet<EnemyParent> _healthBoosted = new HashSet<EnemyParent>();

		private static readonly Dictionary<EnemyParent, int> _baseHealth = new Dictionary<EnemyParent, int>();

		private static readonly Color OrbColor = new Color(1f, 0.55f, 0.05f);

		public static bool IsBossLevel { get; private set; }

		public static bool LootBoosted { get; private set; }

		public static void ResetLevel()
		{
			ReachPatch.Forget();
			IsBossLevel = false;
			_bosses.Clear();
			_healthBoosted.Clear();
			_baseHealth.Clear();
			LootBoosted = false;
		}

		public static void DecideForLevel()
		{
			if (!SemiFunc.RunIsLevel())
			{
				return;
			}
			if (!PluginConfig.Enabled.Value)
			{
				Plugin.LogAlways("[Decide] mod disabled, normal level");
				return;
			}
			RunManager instance = RunManager.instance;
			int num = instance.levelsCompleted + 1;
			string text = BossOdds.MapName(instance.levelCurrent);
			if (num < PluginConfig.EarliestLevel.Value)
			{
				Plugin.LogAlways($"[Decide] level {num} ({text}) below earliest ({PluginConfig.EarliestLevel.Value}), normal level");
				return;
			}
			int num2 = LastBossLevel();
			int value = PluginConfig.CooldownLevels.Value;
			if (num2 > 0 && num - num2 <= value)
			{
				Plugin.LogAlways($"[Decide] level {num} ({text}) on cooldown ({num - num2} since the boss on level {num2}, cooldown {value}), normal level");
				return;
			}
			float num3 = BossOdds.ChanceFor(instance.levelCurrent);
			float num4 = Random.Range(0f, 100f);
			if (num4 < num3)
			{
				IsBossLevel = true;
				StatsManager.instance.runStats["behemothsLastBossLevel"] = num;
				Plugin.LogAlways($"[Decide] level {num} ({text}) is a BOSS LEVEL (rolled {num4:F1} < {num3:F1})");
			}
			else
			{
				Plugin.LogAlways($"[Decide] level {num} ({text}) normal (rolled {num4:F1} >= {num3:F1})");
			}
		}

		private static int LastBossLevel()
		{
			StatsManager instance = StatsManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0;
			}
			if (!instance.runStats.TryGetValue("behemothsLastBossLevel", out var value))
			{
				return 0;
			}
			return value;
		}

		public static void Promote(EnemyParent parent)
		{
			if (!IsBossLevel || (Object)(object)parent == (Object)null)
			{
				return;
			}
			Enemy enemy = parent.Enemy;
			if ((Object)(object)enemy == (Object)null)
			{
				return;
			}
			ApplyScale(enemy);
			ApplyResistance(enemy);
			if (_healthBoosted.Add(parent))
			{
				if (enemy.HasHealth && (Object)(object)enemy.Health != (Object)null)
				{
					_baseHealth[parent] = enemy.Health.health;
				}
				ApplyHealth(enemy);
				ApplyTremor(parent, enemy);
				Plugin.LogInfo("[Promote] " + parent.enemyName + " is now a boss");
			}
			_bosses.Add(enemy);
		}

		public static bool IsBoss(Enemy enemy)
		{
			if ((Object)(object)enemy != (Object)null)
			{
				return _bosses.Contains(enemy);
			}
			return false;
		}

		public static void Retune()
		{
			if (!IsBossLevel || !SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			int num = 0;
			foreach (KeyValuePair<EnemyParent, int> item in _baseHealth)
			{
				EnemyParent key = item.Key;
				if ((Object)(object)key == (Object)null)
				{
					continue;
				}
				Enemy enemy = key.Enemy;
				if (!((Object)(object)enemy == (Object)null))
				{
					RetuneHealth(enemy, item.Value);
					if (((Component)enemy).gameObject.activeInHierarchy)
					{
						RetuneScale(enemy);
						ApplyResistance(enemy);
						num++;
					}
				}
			}
			Plugin.LogAlways($"[Tune] size {PluginConfig.BossSizeMultiplier.Value:0.##} collider {PluginConfig.BossColliderCap.Value:0.##} height {PluginConfig.BossHeightCap.Value:0.##} width {PluginConfig.BossWidthCap.Value:0.##} health x{PluginConfig.BossHealthMultiplier.Value:0.##} resist {PluginConfig.BossDamageResistance.Value:0.##} damage x{PluginConfig.BossDamageMultiplier.Value:0.##}: {num} live boss(es) updated");
		}

		private static void RetuneScale(Enemy enemy)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (!enemy.HasRigidbody || (Object)(object)enemy.Rigidbody == (Object)null)
			{
				return;
			}
			GameObject gameObject = ((Component)enemy.Rigidbody).gameObject;
			ScaleController controller = ScaleManager.GetController(gameObject);
			if ((Object)(object)controller == (Object)null)
			{
				return;
			}
			ScaleOptions val = BuildOptions();
			if (val.Factor <= 1f)
			{
				if (controller.IsScaled)
				{
					ScaleManager.ForceRestore(gameObject);
				}
			}
			else if (!controller.IsScaled)
			{
				ScaleManager.ApplyIfNotScaled(gameObject, val);
			}
			else if (Mathf.Approximately(controller.CurrentOptions.Factor, val.Factor))
			{
				ScaleManager.ForceUpdateOptions(gameObject, val);
			}
			else
			{
				ScaleManager.ForceApply(gameObject, val);
			}
		}

		private static void RetuneHealth(Enemy enemy, int baseHealth)
		{
			if (enemy.HasHealth && !((Object)(object)enemy.Health == (Object)null) && baseHealth > 0 && !KeepsNormalHealth(enemy) && !enemy.Health.dead)
			{
				EnemyHealth health = enemy.Health;
				int num = BossMaxHealth(baseHealth);
				if (num != health.health)
				{
					float num2 = ((health.health > 0) ? ((float)health.healthCurrent / (float)health.health) : 1f);
					health.health = num;
					health.healthCurrent = Mathf.Clamp(Mathf.RoundToInt((float)num * num2), 1, num);
				}
			}
		}

		public static int BoostHit(int damage, int maxHealth)
		{
			float value = PluginConfig.BossDamageMultiplier.Value;
			if (damage <= 0 || value <= 1f)
			{
				return damage;
			}
			int num = Mathf.RoundToInt((float)damage * value);
			float value2 = PluginConfig.BossHitCap.Value;
			if (value2 < 100f)
			{
				int num2 = Mathf.RoundToInt((float)maxHealth * value2 / 100f);
				num = Mathf.Min(num, Mathf.Max(damage, num2));
			}
			return num;
		}

		public static void BoostLevelValuables()
		{
			if (!IsBossLevel || LootBoosted)
			{
				return;
			}
			LootBoosted = true;
			float value = PluginConfig.ValuableValueMultiplier.Value;
			if (value == 1f)
			{
				return;
			}
			int num = 0;
			float num2 = 0f;
			ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>();
			foreach (ValuableObject val in array)
			{
				if (!((Object)(object)val == (Object)null) && !IsEnemyOrb(val) && val.dollarValueSet)
				{
					float dollarValueCurrent = val.dollarValueCurrent;
					float num3 = Mathf.Round(dollarValueCurrent * value);
					SetValue(val, num3);
					num2 += num3 - dollarValueCurrent;
					num++;
				}
			}
			if (num2 != 0f && (Object)(object)RoundDirector.instance != (Object)null)
			{
				RoundDirector instance = RoundDirector.instance;
				instance.haulGoalMax += (int)num2;
			}
			Plugin.LogAlways($"[Loot] boosted {num} valuable(s) x{value} (+{num2:F0})");
		}

		public static void BoostLateValuable(ValuableObject valuable)
		{
			if (IsBossLevel && LootBoosted)
			{
				float value = PluginConfig.ValuableValueMultiplier.Value;
				if (value != 1f)
				{
					float num = Mathf.Round(valuable.dollarValueCurrent * value);
					SetValue(valuable, num);
					Plugin.LogInfo($"[Loot] late valuable {((Object)valuable).name} x{value} -> {num}");
				}
			}
		}

		public static void SetValue(ValuableObject valuable, float value)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (SemiFunc.IsMultiplayer())
			{
				if ((Object)(object)valuable.photonView != (Object)null && valuable.photonView.ViewID != 0)
				{
					valuable.photonView.RPC("DollarValueSetRPC", (RpcTarget)0, new object[1] { value });
				}
			}
			else
			{
				valuable.DollarValueSetRPC(value, default(PhotonMessageInfo));
			}
		}

		public static bool IsEnemyOrb(ValuableObject valuable)
		{
			return (Object)(object)OrbOf(valuable) != (Object)null;
		}

		private static EnemyValuable? OrbOf(ValuableObject valuable)
		{
			return ((Component)valuable).GetComponentInParent<EnemyValuable>() ?? ((Component)valuable).GetComponentInChildren<EnemyValuable>();
		}

		public static void TintOrb(ValuableObject valuable)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.OrbGlow.Value)
			{
				return;
			}
			EnemyValuable val = OrbOf(valuable);
			if (!((Object)(object)val == (Object)null))
			{
				val.fresnelColorDefault = OrbColor;
				val.fresnelColorIndestructible = OrbColor;
				if ((Object)(object)val.outerMaterial != (Object)null)
				{
					val.outerMaterial.SetColor("_FresnelColor", OrbColor);
				}
			}
		}

		private static void ApplyScale(Enemy enemy)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (enemy.HasRigidbody && !((Object)(object)enemy.Rigidbody == (Object)null))
			{
				ScaleOptions val = BuildOptions();
				if (!(val.Factor <= 1f))
				{
					ScaleManager.ApplyIfNotScaled(((Component)enemy.Rigidbody).gameObject, val);
				}
			}
		}

		private static ScaleOptions BuildOptions()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			ScaleOptions growth = ScaleOptions.Growth;
			growth.Factor = PluginConfig.BossSizeMultiplier.Value;
			growth.AllowedTargets = (ScaleTargets)2;
			growth.EnemyPhysicalFactorCap = PluginConfig.BossColliderCap.Value;
			growth.EnemyWidthFactorCap = PluginConfig.BossWidthCap.Value;
			growth.EnemyHeightFactorCap = PluginConfig.BossHeightCap.Value;
			growth.EnemyNavRadiusFactorCap = 1f;
			growth.RejectExternalApply = true;
			growth.IgnoreBonkExpand = true;
			return growth;
		}

		private static void ApplyResistance(Enemy enemy)
		{
			if (enemy.HasHealth && !((Object)(object)enemy.Health == (Object)null))
			{
				float num = Mathf.Clamp(PluginConfig.BossDamageResistance.Value, 0f, 0.9f);
				enemy.Health.OverrideDamageResistance(num, (num > 0f) ? 1E+09f : 0f);
			}
		}

		private static void ApplyHealth(Enemy enemy)
		{
			if (enemy.HasHealth && !((Object)(object)enemy.Health == (Object)null) && !(PluginConfig.BossHealthMultiplier.Value <= 1f) && !KeepsNormalHealth(enemy))
			{
				int num = BossMaxHealth(enemy.Health.health);
				enemy.Health.health = num;
				enemy.Health.healthCurrent = num;
				Plugin.LogVerbose($"[Promote] health -> {num}");
			}
		}

		private static int BossMaxHealth(int baseHealth)
		{
			return Mathf.Max(1, Mathf.RoundToInt((float)baseHealth * Mathf.Max(1f, PluginConfig.BossHealthMultiplier.Value)));
		}

		private static bool KeepsNormalHealth(Enemy enemy)
		{
			return (Object)(object)((Component)enemy).GetComponentInChildren<EnemyTick>(true) != (Object)null;
		}

		public static float BodyWidth()
		{
			float value = PluginConfig.BossSizeMultiplier.Value;
			float value2 = PluginConfig.BossWidthCap.Value;
			float value3 = PluginConfig.BossColliderCap.Value;
			if (value2 > 0f && value > value2)
			{
				return Mathf.Max(1f, value2);
			}
			if (value3 > 0f && value > value3)
			{
				return Mathf.Max(1f, value3);
			}
			return Mathf.Max(1f, value);
		}

		private static void ApplyTremor(EnemyParent parent, Enemy enemy)
		{
			if (enemy.HasRigidbody && !((Object)(object)enemy.Rigidbody == (Object)null))
			{
				GameObject gameObject = ((Component)enemy.Rigidbody).gameObject;
				if (!((Object)(object)gameObject.GetComponent<BehemothTremor>() != (Object)null))
				{
					gameObject.AddComponent<BehemothTremor>().Setup(parent, enemy.Rigidbody.rb);
				}
			}
		}
	}
	internal class LiveTuning : MonoBehaviour
	{
		private const float PollSeconds = 1f;

		private const float SettleSeconds = 0.3f;

		private ConfigFile _config;

		private DateTime _seenWrite;

		private float _nextPoll;

		private float _applyAt = -1f;

		private float _ownWritesUntil = -1f;

		public void Setup(ConfigFile config)
		{
			_config = config;
			_seenWrite = WriteTime();
			_config.SettingChanged += OnSettingChanged;
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			_applyAt = Time.unscaledTime + 0.3f;
			_ownWritesUntil = Time.unscaledTime + 1f + 0.3f;
		}

		private void Update()
		{
			if (Time.unscaledTime >= _nextPoll)
			{
				_nextPoll = Time.unscaledTime + 1f;
				DateTime dateTime = WriteTime();
				if (dateTime != _seenWrite)
				{
					if (Time.unscaledTime < _ownWritesUntil)
					{
						_seenWrite = dateTime;
					}
					else
					{
						try
						{
							_config.Reload();
						}
						catch (IOException)
						{
							return;
						}
						_seenWrite = WriteTime();
						Plugin.LogAlways("[Tune] config file changed, reloaded");
					}
				}
			}
			if (_applyAt >= 0f && Time.unscaledTime >= _applyAt)
			{
				_applyAt = -1f;
				BossRoundService.Retune();
			}
		}

		private DateTime WriteTime()
		{
			if (!File.Exists(_config.ConfigFilePath))
			{
				return DateTime.MinValue;
			}
			return File.GetLastWriteTimeUtc(_config.ConfigFilePath);
		}
	}
}
namespace Behemoths.Patches
{
	[HarmonyPatch(typeof(EnemyDirector), "Awake")]
	internal static class EnemyDirectorAwakePatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			BossRoundService.ResetLevel();
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				BossRoundService.DecideForLevel();
			}
		}
	}
	[HarmonyPatch(typeof(EnemyParent), "SpawnRPC")]
	internal static class EnemySpawnPatch
	{
		[HarmonyPostfix]
		public static void Postfix(EnemyParent __instance)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.RunIsLevel())
			{
				BossRoundService.Promote(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyValuable), "Start")]
	internal static class EnemyValuableSpawnPatch
	{
		[HarmonyPostfix]
		public static void Postfix(EnemyValuable __instance)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && BossRoundService.IsBossLevel)
			{
				Rigidbody componentInChildren = ((Component)__instance).GetComponentInChildren<Rigidbody>();
				if ((Object)(object)componentInChildren != (Object)null && !componentInChildren.isKinematic)
				{
					componentInChildren.collisionDetectionMode = (CollisionDetectionMode)2;
				}
			}
		}
	}
	[HarmonyPatch(typeof(HurtCollider), "PlayerHurt")]
	internal static class HurtColliderPlayerHurtPatch
	{
		[HarmonyPrefix]
		public static void Prefix(HurtCollider __instance, PlayerAvatar _player)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			if (!GameManager.Multiplayer() || !PhotonNetwork.IsMasterClient || !BossRoundService.IsBossLevel || (Object)(object)_player == (Object)null || _player.photonView.IsMine || !__instance.playerLogic || __instance.detectionOnly || __instance.playerKill || __instance.ignorePlayers.Contains(_player))
			{
				return;
			}
			Enemy enemyHost = __instance.enemyHost;
			if ((Object)(object)enemyHost == (Object)null || !BossRoundService.IsBoss(enemyHost))
			{
				return;
			}
			int playerDamage = __instance.playerDamage;
			if (playerDamage <= 0)
			{
				return;
			}
			PlayerHealth playerHealth = _player.playerHealth;
			if ((Object)(object)playerHealth == (Object)null || playerHealth.health <= 0 || _player.deadSet)
			{
				return;
			}
			int num = BossRoundService.BoostHit(playerDamage, playerHealth.maxHealth) - playerDamage;
			if (num > 0)
			{
				Vector3 position = _player.PlayerVisionTarget.VisionTransform.position;
				if (__instance.CanHit(((Component)_player).gameObject, __instance.playerDamageCooldown, __instance.playerRayCast, position, (HitType)0))
				{
					int num2 = SemiFunc.EnemyGetIndex(enemyHost);
					Plugin.LogInfo($"[Hit] {((Object)enemyHost).name} hurt {_player.playerName} +{num} on top of {playerDamage}");
					playerHealth.photonView.RPC("HurtOtherRPC", _player.photonView.Owner, new object[5]
					{
						num,
						Vector3.zero,
						true,
						num2,
						false
					});
				}
			}
		}
	}
	[HarmonyPatch(typeof(MoonUI), "Check")]
	internal static class MoonUIPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(MoonUI __instance)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer() || !SemiFunc.RunIsLevel())
			{
				return true;
			}
			if (!BossRoundService.IsBossLevel || !PluginConfig.AnnounceBossLevel.Value)
			{
				return true;
			}
			return !BossAnnouncer.Show(__instance);
		}
	}
	[HarmonyPatch(typeof(PlayerHealth), "HurtOther")]
	internal static class PlayerHealthHurtOtherPatch
	{
		[HarmonyPrefix]
		public static void Prefix(PlayerHealth __instance, ref int damage, int enemyIndex)
		{
			if (GameManager.Multiplayer() && !__instance.photonView.IsMine && SemiFunc.IsMasterClientOrSingleplayer() && BossRoundService.IsBossLevel && enemyIndex >= 0 && damage > 0)
			{
				Enemy val = SemiFunc.EnemyGetFromIndex(enemyIndex);
				if (!((Object)(object)val == (Object)null) && BossRoundService.IsBoss(val))
				{
					int num = BossRoundService.BoostHit(damage, __instance.maxHealth);
					Plugin.LogInfo($"[Hit] {((Object)val).name} hurt other {damage} -> {num}");
					damage = num;
				}
			}
		}
	}
	[HarmonyPatch(typeof(PlayerHealth), "Hurt")]
	internal static class PlayerHealthHurtPatch
	{
		[HarmonyPrefix]
		public static void Prefix(PlayerHealth __instance, ref int damage, int enemyIndex)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && BossRoundService.IsBossLevel && enemyIndex >= 0 && damage > 0 && (!GameManager.Multiplayer() || __instance.photonView.IsMine))
			{
				Enemy val = SemiFunc.EnemyGetFromIndex(enemyIndex);
				if (!((Object)(object)val == (Object)null) && BossRoundService.IsBoss(val))
				{
					int num = BossRoundService.BoostHit(damage, __instance.maxHealth);
					Plugin.LogInfo($"[Hit] {((Object)val).name} hurt host {damage} -> {num}");
					damage = num;
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class ReachPatch
	{
		private static readonly (string Type, string Method, float[] Metres)[] Targets = new(string, string, float[])[7]
		{
			("EnemyRobe", "StateTargetPlayer", new float[1] { 2f }),
			("EnemyRobe", "StateLookUnder", new float[1] { 2.5f }),
			("EnemyAnimal", "StateGoToPlayer", new float[1] { 3f }),
			("EnemyFloater", "StateNotice", new float[1] { 2.5f }),
			("EnemySlowWalker", "StateLookUnder", new float[1] { 3f }),
			("EnemyUpscream", "StateGoToPlayer", new float[1] { 1.5f }),
			("EnemyValuableThrower", "StateTargetPlayer", new float[2] { 3f, 5f })
		};

		private static readonly Dictionary<MonoBehaviour, Enemy?> owners = new Dictionary<MonoBehaviour, Enemy>();

		private static IEnumerable<MethodBase> TargetMethods()
		{
			(string Type, string Method, float[] Metres)[] targets = Targets;
			for (int i = 0; i < targets.Length; i++)
			{
				(string Type, string Method, float[] Metres) tuple = targets[i];
				string item = tuple.Type;
				string item2 = tuple.Method;
				MethodBase methodBase = AccessTools.Method(AccessTools.TypeByName(item), item2, (Type[])null, (Type[])null);
				if (methodBase != null)
				{
					yield return methodBase;
					continue;
				}
				Plugin.LogAlways("[Reach] " + item + "." + item2 + " is not in this build, its reach stays vanilla");
			}
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
		{
			float[] metres = Targets.First(((string Type, string Method, float[] Metres) t) => t.Type == original.DeclaringType.Name && t.Method == original.Name).Metres;
			MethodInfo scale = AccessTools.Method(typeof(ReachPatch), "Scale", (Type[])null, (Type[])null);
			MethodInfo distance = AccessTools.Method(typeof(Vector3), "Distance", new Type[2]
			{
				typeof(Vector3),
				typeof(Vector3)
			}, (Type[])null);
			int swapped = 0;
			bool afterDistance = false;
			foreach (CodeInstruction code in instructions)
			{
				yield return code;
				if (afterDistance && code.opcode == OpCodes.Ldc_R4)
				{
					object operand = code.operand;
					if (operand is float)
					{
						float f = (float)operand;
						if (metres.Any((float m) => Mathf.Approximately(m, f)))
						{
							yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
							yield return new CodeInstruction(OpCodes.Call, (object)scale);
							swapped++;
						}
					}
				}
				afterDistance = code.opcode == OpCodes.Call && code.operand is MethodInfo methodInfo && methodInfo == distance;
			}
			Plugin.LogInfo($"[Reach] {original.DeclaringType.Name}.{original.Name}: {swapped} distance(s) now follow boss size");
		}

		public static float Scale(float metres, MonoBehaviour self)
		{
			if (!BossRoundService.IsBossLevel || (Object)(object)self == (Object)null)
			{
				return metres;
			}
			if (!owners.TryGetValue(self, out Enemy value))
			{
				value = ((Component)self).GetComponent<Enemy>() ?? ((Component)self).GetComponentInParent<Enemy>();
				owners[self] = value;
			}
			if ((Object)(object)value == (Object)null || !BossRoundService.IsBoss(value))
			{
				return metres;
			}
			return metres * BossRoundService.BodyWidth();
		}

		internal static void Forget()
		{
			owners.Clear();
		}
	}
	[HarmonyPatch(typeof(RoundDirector), "StartRoundLogic")]
	internal static class RoundDirectorStartPatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				BossRoundService.BoostLevelValuables();
			}
		}
	}
	[HarmonyPatch(typeof(ValuableObject), "DollarValueSetLogic")]
	internal static class ValuablePatch
	{
		[HarmonyPrefix]
		public static void Prefix(ValuableObject __instance, out bool __state)
		{
			__state = __instance.dollarValueSet;
		}

		[HarmonyPostfix]
		public static void Postfix(ValuableObject __instance, bool __state)
		{
			if (__state || !SemiFunc.IsMasterClientOrSingleplayer() || !SemiFunc.RunIsLevel() || !BossRoundService.IsBossLevel)
			{
				return;
			}
			if (!BossRoundService.IsEnemyOrb(__instance))
			{
				BossRoundService.BoostLateValuable(__instance);
				return;
			}
			float value = PluginConfig.OrbValueMultiplier.Value;
			if (value != 1f)
			{
				float num = Mathf.Round(__instance.dollarValueCurrent * value);
				BossRoundService.SetValue(__instance, num);
				Plugin.LogVerbose($"[Loot] orb value x{value} -> {num}");
			}
			GrowOrb(__instance);
			BossRoundService.TintOrb(__instance);
		}

		private static void GrowOrb(ValuableObject valuable)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: 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)
			PhysGrabObject physGrabObject = valuable.physGrabObject;
			if ((Object)(object)physGrabObject == (Object)null)
			{
				return;
			}
			Rigidbody component = ((Component)physGrabObject).GetComponent<Rigidbody>();
			float num = (((Object)(object)component != (Object)null) ? component.mass : 0f);
			if ((Object)(object)component != (Object)null && !component.isKinematic)
			{
				component.collisionDetectionMode = (CollisionDetectionMode)2;
			}
			float value = PluginConfig.OrbSizeMultiplier.Value;
			if (value > 1f)
			{
				ScaleOptions growth = ScaleOptions.Growth;
				growth.Factor = value;
				growth.AllowedTargets = (ScaleTargets)8;
				growth.RejectExternalApply = true;
				growth.IgnoreBonkExpand = true;
				growth.SuppressValueDropExpand = true;
				ScaleManager.ApplyIfNotScaled(((Component)physGrabObject).gameObject, growth);
				if ((Object)(object)component != (Object)null)
				{
					component.position += Vector3.up * 0.5f * (value - 1f);
				}
			}
			float value2 = PluginConfig.OrbWeightMultiplier.Value;
			if (value2 > 1f && (Object)(object)component != (Object)null && num > 0f)
			{
				Plugin.LogVerbose(string.Format(arg2: physGrabObject.massOriginal = (component.mass = num * value2), format: "[Loot] boss orb size x{0}, weight x{1} ({2:F1})", arg0: value, arg1: value2));
			}
			float value3 = PluginConfig.OrbDurability.Value;
			if (value3 > 1f)
			{
				PhysGrabObjectImpactDetector componentInChildren = ((Component)physGrabObject).GetComponentInChildren<PhysGrabObjectImpactDetector>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.fragilityMultiplier = 1f / value3;
				}
			}
		}
	}
}
namespace Behemoths.Configuration
{
	public enum VerbosityLevel
	{
		Off,
		Debug,
		Verbose
	}
	internal static class PluginConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<bool> AnnounceBossLevel;

		public static ConfigEntry<VerbosityLevel> LoggingLevel;

		public static ConfigEntry<float> BaseChance;

		public static ConfigEntry<float> MaxChance;

		public static ConfigEntry<int> CooldownLevels;

		public static ConfigEntry<int> EarliestLevel;

		public static ConfigEntry<float> BossSizeMultiplier;

		public static ConfigEntry<float> BossHealthMultiplier;

		public static ConfigEntry<float> BossDamageMultiplier;

		public static ConfigEntry<float> BossDamageResistance;

		public static ConfigEntry<float> BossHitCap;

		public static ConfigEntry<bool> BossTremors;

		public static ConfigEntry<float> OrbValueMultiplier;

		public static ConfigEntry<float> ValuableValueMultiplier;

		public static ConfigEntry<bool> OrbGlow;

		public static ConfigEntry<float> MapManor;

		public static ConfigEntry<float> MapArctic;

		public static ConfigEntry<float> MapWizard;

		public static ConfigEntry<float> MapMuseum;

		public static ConfigEntry<float> MapOther;

		public static ConfigEntry<float> BossColliderCap;

		public static ConfigEntry<float> BossHeightCap;

		public static ConfigEntry<float> BossWidthCap;

		public static ConfigEntry<float> OrbSizeMultiplier;

		public static ConfigEntry<float> OrbWeightMultiplier;

		public static ConfigEntry<float> OrbDurability;

		public static void Init(ConfigFile config)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Expected O, but got Unknown
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Expected O, but got Unknown
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Expected O, but got Unknown
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Expected O, but got Unknown
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Expected O, but got Unknown
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Expected O, but got Unknown
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Expected O, but got Unknown
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Expected O, but got Unknown
			//IL_033a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Expected O, but got Unknown
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Expected O, but got Unknown
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b4: Expected O, but got Unknown
			//IL_03e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ec: Expected O, but got Unknown
			//IL_041a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Expected O, but got Unknown
			//IL_0452: Unknown result type (might be due to invalid IL or missing references)
			//IL_045c: Expected O, but got Unknown
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0494: Expected O, but got Unknown
			//IL_04c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cc: Expected O, but got Unknown
			//IL_04fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0504: Expected O, but got Unknown
			//IL_0532: Unknown result type (might be due to invalid IL or missing references)
			//IL_053c: Expected O, but got Unknown
			Enabled = config.Bind<bool>("General", "Enabled", true, "Turns boss levels on or off.");
			AnnounceBossLevel = config.Bind<bool>("General", "AnnounceBossLevel", true, "Shows BOSS LEVEL on the game's moon-phase screen once the level loads, with this level's numbers. Only the host sees it. On a level that is also a moon change it replaces the moon screen, and the moon's modifiers still apply.");
			LoggingLevel = config.Bind<VerbosityLevel>("General", "LogLevel", VerbosityLevel.Off, "Off logs a few lines per level: the boss roll, the loot boost and the popup. Debug adds every Behemoth, hit and orb. Verbose logs everything.");
			BaseChance = config.Bind<float>("Boss Levels", "BaseChance", 10f, new ConfigDescription("Percent chance a level is a boss level at the start of a run. It climbs toward MaxChance along the game's difficulty curve.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			MaxChance = config.Bind<float>("Boss Levels", "MaxChance", 60f, new ConfigDescription("Percent chance from level 10 on. If it is lower than BaseChance, BaseChance wins. A chance of 100 means every eligible level, on any map whose odds are above 0.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			CooldownLevels = config.Bind<int>("Boss Levels", "CooldownLevels", 3, new ConfigDescription("Normal levels that must pass after a boss level before another can roll. 0 allows back to back. Saved with the run.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>()));
			EarliestLevel = config.Bind<int>("Boss Levels", "EarliestLevel", 3, new ConfigDescription("First level that can be a boss level. The game holds its nastier monsters back for the first two levels too.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
			BossSizeMultiplier = config.Bind<float>("Boss Monsters", "SizeMultiplier", 1.4f, new ConfigDescription("How much bigger a Behemoth is. Its body grows wider with it but stays door height, so it can still follow you through the building. Players need ScalerCore to see the size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 4f), Array.Empty<object>()));
			BossHealthMultiplier = config.Bind<float>("Boss Monsters", "HealthMultiplier", 3f, new ConfigDescription("Health compared to a normal monster of the same kind. The Tick keeps its normal health.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			BossDamageMultiplier = config.Bind<float>("Boss Monsters", "DamageMultiplier", 2f, new ConfigDescription("How much harder a Behemoth hits. Applies to every player, with or without the mod.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			BossDamageResistance = config.Bind<float>("Boss Monsters", "DamageResistance", 0.5f, new ConfigDescription("Share of incoming damage a Behemoth ignores, on top of its extra health. 0.5 takes half damage, 0 turns it off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.9f), Array.Empty<object>()));
			BossHitCap = config.Bind<float>("Boss Monsters", "HitCap", 75f, new ConfigDescription("The most one boosted hit can take, as a percent of a player's max health, unless the normal hit already took more. 100 turns the cap off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 100f), Array.Empty<object>()));
			BossTremors = config.Bind<bool>("Boss Monsters", "Tremors", true, "Shakes the host's camera when a Behemoth moves nearby.");
			OrbValueMultiplier = config.Bind<float>("Boss Loot", "OrbValueMultiplier", 2.5f, new ConfigDescription("Cash value of the orb a Behemoth drops, compared to a normal orb. Every player gets it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			ValuableValueMultiplier = config.Bind<float>("Boss Loot", "ValuableMultiplier", 1.5f, new ConfigDescription("Cash value of every other valuable on a boss level. The extraction goal does not go up, so the extra is profit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>()));
			OrbGlow = config.Bind<bool>("Boss Loot", "OrbGlow", true, "Amber glow on Behemoth orbs. Only the host sees it.");
			MapManor = config.Bind<float>("Map Odds", "Manor", 1f, new ConfigDescription("Multiplier on the boss-level chance at Headman Manor. 0 means never.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			MapArctic = config.Bind<float>("Map Odds", "Arctic", 0.75f, new ConfigDescription("Multiplier on the chance at McJannek Station. Lower by default, the corridors are tight.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			MapWizard = config.Bind<float>("Map Odds", "Wizard", 1.25f, new ConfigDescription("Multiplier on the chance at Swiftbroom Academy. Higher by default, the halls are big.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			MapMuseum = config.Bind<float>("Map Odds", "Museum", 1.25f, new ConfigDescription("Multiplier on the chance at the Museum of Human Art. Higher by default, the galleries are open.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			MapOther = config.Bind<float>("Map Odds", "OtherMaps", 1f, new ConfigDescription("Multiplier on the chance on any other map, modded ones included.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			BossColliderCap = config.Bind<float>("Tuning", "ColliderCap", 0f, new ConfigDescription("Holds the whole body (hitbox, grab body, reach) at this multiple of vanilla while the look keeps growing. 0 lets the body follow SizeMultiplier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 4f), Array.Empty<object>()));
			BossHeightCap = config.Bind<float>("Tuning", "HeightCap", 1f, new ConfigDescription("How tall the body may get, as a multiple of vanilla. 1 keeps door height. 0 lets it grow as tall as it looks, and a big one gets stuck in doorways.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 4f), Array.Empty<object>()));
			BossWidthCap = config.Bind<float>("Tuning", "WidthCap", 0f, new ConfigDescription("How wide the body may get, as a multiple of vanilla. 0 follows the look. Try 1.5 if a map's doorways give wide Behemoths trouble.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 4f), Array.Empty<object>()));
			OrbSizeMultiplier = config.Bind<float>("Tuning", "OrbSizeMultiplier", 2f, new ConfigDescription("How big a Behemoth orb is. Players need ScalerCore to see the size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 6f), Array.Empty<object>()));
			OrbWeightMultiplier = config.Bind<float>("Tuning", "OrbWeightMultiplier", 2.5f, new ConfigDescription("How heavy a Behemoth orb is, set apart from its size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			OrbDurability = config.Bind<float>("Tuning", "OrbDurability", 5f, new ConfigDescription("How much knocking around a Behemoth orb takes before it loses value. 5 is about five times a normal valuable, 1 is normal.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
		}
	}
}