Decompiled source of Saiyaheim v0.9.1

BepInEx/plugins/Saiyaheim/Saiyaheim.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
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 Saiyaheim.Attacks;
using Saiyaheim.Debugging;
using Saiyaheim.Flight;
using Saiyaheim.Kaioken;
using Saiyaheim.Ki;
using Saiyaheim.Net;
using Saiyaheim.Power;
using Saiyaheim.Runes;
using Saiyaheim.Scouter;
using Saiyaheim.Transformations;
using Saiyaheim.Util;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
using Valheim.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Saiyaheim")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Mod de Dragon Ball para Valheim")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0+4f0f05b946c2478b59047ae24417ecdcb25dfb4f")]
[assembly: AssemblyProduct("Saiyaheim")]
[assembly: AssemblyTitle("Saiyaheim")]
[assembly: AssemblyVersion("0.3.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 Saiyaheim
{
	[BepInPlugin("com.hman.saiyaheim", "Saiyaheim", "0.9.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class SaiyaheimPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.hman.saiyaheim";

		public const string PluginName = "Saiyaheim";

		public const string PluginVersion = "0.9.1";

		private Harmony _harmony;

		private ConfigReloader _configReloader;

		internal static SaiyaheimPlugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static void LogVerbose(string message)
		{
			if (SaiyaheimConfig.VerboseLogging != null && SaiyaheimConfig.VerboseLogging.Value)
			{
				Log.LogInfo((object)message);
			}
		}

		private void Awake()
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			SaiyaheimConfig.Init(((BaseUnityPlugin)this).Config);
			_configReloader = new ConfigReloader(((BaseUnityPlugin)this).Config, delegate
			{
				KiHud.OnConfigReloaded();
				PowerHud.OnConfigReloaded();
				EnemyPowerHud.OnConfigReloaded();
			});
			PowerSkill.Register();
			FlightSkill.Register();
			KaiokenSkill.Register();
			TransformationRegistry.Register();
			CustomHair.Register();
			ScouterRegistry.Register();
			_harmony = new Harmony("com.hman.saiyaheim");
			_harmony.PatchAll(typeof(SaiyaheimPlugin).Assembly);
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new KiCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PowerCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new FlightCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BlockCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new TransformCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AttackCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new NetCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new KaiokenCommand());
			Log.LogInfo((object)"Saiyaheim v0.9.1 loaded.");
		}

		private void Update()
		{
			float deltaTime = Time.deltaTime;
			_configReloader.Update(deltaTime);
			KiManager.Update(deltaTime);
			KiBodyManager.Update(Player.m_localPlayer);
			FlightManager.Update(Player.m_localPlayer);
			TransformationManager.Update(Player.m_localPlayer);
			KaiokenManager.Update(Player.m_localPlayer);
			KiAttackManager.Update(Player.m_localPlayer, deltaTime);
			PublishNetState(Player.m_localPlayer);
			RemoteEffects.Update();
			ScouterReader.Update(Player.m_localPlayer);
			ScouterTooltip.Update(Player.m_localPlayer, deltaTime);
			KiHud.Update();
			PowerHud.Update();
		}

		private static void PublishNetState(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				NetState.Publish(player, KiManager.IsEnabled, FlightManager.IsFlying(player), KiManager.IsCharging, KiBeamCharge.IsCharging(player), KiBeamCharge.IsFull(player), FlightManager.SteersByAim(player), TransformationRegistry.IndexOf(TransformationRegistry.GetActive(player)), KiAttackRegistry.IndexOf(KiBeamCharge.Current), KaiokenRegistry.GetActive(player)?.Index ?? (-1));
				NetState.PublishRating(player, PowerRating.GetRaw((Character)(object)player));
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public enum HudTextAlign
	{
		Left,
		Center,
		Right
	}
	public enum ChargePose
	{
		TwoHandBeam,
		OverheadDisc
	}
	public enum KiDamageType
	{
		Slash,
		Pierce
	}
	public enum ImpactTintTarget
	{
		Light,
		Everything
	}
	public enum EffectAnchor
	{
		Body,
		RightHand,
		LeftHand
	}
	public static class SaiyaheimConfig
	{
		public class TransformationConfig
		{
			public ConfigEntry<float> PowerMultiplier { get; internal set; }

			public ConfigEntry<float> KiDrainPerSecond { get; internal set; }

			public ConfigEntry<float> PunchSlashFraction { get; internal set; }

			public ConfigEntry<float> PunchLightningFraction { get; internal set; }

			public ConfigEntry<float> CarryWeightBonus { get; internal set; }

			public ConfigEntry<float> HealthRegenSpeed { get; internal set; }

			public ConfigEntry<bool> HealthRegenIgnoresBlockers { get; internal set; }

			public ConfigEntry<float> HealthRegenMinimum { get; internal set; }

			public ConfigEntry<DamageModifier> BluntResistance { get; internal set; }

			public ConfigEntry<float> CombatKiCostScale { get; internal set; }

			public ConfigEntry<float> MasteryDrainReduction { get; internal set; }

			public ConfigEntry<float> KaiokenPenalty { get; internal set; }

			public ConfigEntry<float> KaiokenPenaltyZeroAtMastery { get; internal set; }

			public ConfigEntry<string> RequiredGlobalKey { get; internal set; }

			public string HairItem { get; internal set; }

			public string HairColor { get; internal set; }

			public float HairColorIntensity { get; internal set; }

			public string AuraColor { get; internal set; }

			public bool LightningEnabled { get; internal set; }

			public string LightningColor { get; internal set; }

			public float GlowIntensity { get; internal set; }

			public string GlowColor { get; internal set; }
		}

		public class KaiokenTierConfig
		{
			public ConfigEntry<float> PowerMultiplier { get; internal set; }

			public ConfigEntry<float> SafeLevel { get; internal set; }

			public ConfigEntry<float> StaminaPerSecond { get; internal set; }

			public ConfigEntry<float> HealthPerSecondPerLevel { get; internal set; }

			public ConfigEntry<float> MoveSpeedBonus { get; internal set; }

			public ConfigEntry<float> AttackSpeedBonus { get; internal set; }

			public ConfigEntry<float> MaxKiBonus { get; internal set; }

			public ConfigEntry<Biome> LearnBiome { get; internal set; }

			public ConfigEntry<float> LearnWeight { get; internal set; }

			public string AuraColor { get; internal set; }

			public float GlowIntensity { get; internal set; }
		}

		public class KiAttackConfig
		{
			public ConfigEntry<float> DamageBase { get; internal set; }

			public ConfigEntry<float> DamageFromPower { get; internal set; }

			public ConfigEntry<float> KiCost { get; internal set; }

			public ConfigEntry<float> Cooldown { get; internal set; }

			public ConfigEntry<float> Knockback { get; internal set; }

			public ConfigEntry<float> ImpactRadius { get; internal set; }

			public string ProjectilePrefab { get; internal set; }

			public string ImpactEffect { get; internal set; }

			public string ImpactEffectStrip { get; internal set; }

			public string ImpactColor { get; internal set; }

			public ImpactTintTarget ImpactColorTarget { get; internal set; }

			public bool ProjectileLingerOnHit { get; internal set; }

			public ConfigEntry<float> ProjectileSpeed { get; internal set; }

			public ConfigEntry<float> ProjectileLifetime { get; internal set; }

			public float ProjectileScale { get; internal set; }

			public string ProjectileColor { get; internal set; }

			public ConfigEntry<int> BeamCount { get; internal set; }

			public ConfigEntry<float> BeamInterval { get; internal set; }

			public ConfigEntry<float> TierPowerMultiplier { get; internal set; }

			public ConfigEntry<float> ChargeTime { get; internal set; }

			public ConfigEntry<float> MinChargeRatio { get; internal set; }

			public float ChargeMinScale { get; internal set; }

			public ConfigEntry<float> ChargeMinDamage { get; internal set; }

			public string ChargeEffectPrefab { get; internal set; }

			public string ChargeEffectColor { get; internal set; }

			public float ChargeEffectScale { get; internal set; }

			public float ChargeEffectHeight { get; internal set; }

			public float ChargeEffectSide { get; internal set; }

			public float ChargeEffectForward { get; internal set; }

			public EffectAnchor ChargeEffectAnchor { get; internal set; }

			public string ChargeBallPrefab { get; internal set; }

			public string ChargeBallColor { get; internal set; }

			public float ChargeBallScale { get; internal set; }

			public string ChargeBallStrip { get; internal set; }

			public string ChargeFullEffectPrefab { get; internal set; }

			public string ChargeFullEffectColor { get; internal set; }

			public float ChargeFullEffectScale { get; internal set; }

			public bool ChargeFullEffectLoop { get; internal set; }

			public bool ChargeFullEffectReplaces { get; internal set; }

			public ConfigEntry<string> RequiredGlobalKey { get; internal set; }

			public ConfigEntry<Biome> LearnBiome { get; internal set; }

			public ConfigEntry<float> LearnWeight { get; internal set; }

			public ChargePose ChargePose { get; internal set; }

			public KiDamageType DamageType { get; internal set; }

			public bool HoldStillWhileCharging { get; internal set; } = true;

			public bool Pierce { get; internal set; }

			public ConfigEntry<float> HitRadius { get; internal set; }

			public KiDiscConfig Disc { get; internal set; }

			public OverheadPoseConfig OverheadPose { get; internal set; }

			public float GetKnockback()
			{
				return Knockback?.Value ?? 0f;
			}

			public float GetImpactRadius()
			{
				return ImpactRadius?.Value ?? 0f;
			}

			public int GetBeamCount()
			{
				return BeamCount?.Value ?? 1;
			}

			public float GetBeamInterval()
			{
				return BeamInterval?.Value ?? 0.05f;
			}
		}

		public class KiDiscConfig
		{
			public float Radius { get; internal set; }

			public float Thickness { get; internal set; }

			public int Teeth { get; internal set; }

			public float ToothDepth { get; internal set; }

			public float CoreWhiteness { get; internal set; }

			public float Glow { get; internal set; }

			public float Opacity { get; internal set; }

			public float SpinSpeed { get; internal set; }

			public float LightIntensity { get; internal set; }

			public float LightRange { get; internal set; }

			public float HoldHeight { get; internal set; }
		}

		public class OverheadPoseConfig
		{
			public float BlendSeconds { get; internal set; }

			public float ArmHeight { get; internal set; }

			public float ArmForward { get; internal set; }

			public float ArmTwist { get; internal set; }

			public float ForearmTwist { get; internal set; }

			public float ElbowStretch { get; internal set; }

			public float ShoulderLift { get; internal set; }

			public float WristBend { get; internal set; }

			public float HandOpen { get; internal set; }
		}

		private const string SecGeral = "1 - General";

		private const string SecKi = "2 - Ki";

		private const string SecCombat = "2.1 - Combat";

		private const string SecEnvironment = "2.2 - Environment";

		private const string SecTransformations = "3 - Transformations";

		private const string SecSsj = "3.1 - SSJ";

		private const string SecSsj2 = "3.2 - SSJ2";

		private const string SecSsj3 = "3.3 - SSJ3";

		private const string SecSsjGod = "3.4 - SSJ God";

		private const string SecKaioken = "3.5 - Kaioken";

		private const string SecRunestones = "4 - Runestones";

		private const string SecKiBlast = "4.1 - Ki Blast";

		private const string SecKamehameha = "4.2 - Kamehameha";

		private const string SecKienzan = "4.3 - Kienzan";

		private const string SecFlight = "5 - Flight";

		private const string SecPower = "6 - Battle Power";

		private const string SecPowerSkill = "6.1 - Power Level";

		private const string SecHud = "7 - HUD";

		private const string SecDebug = "9 - Debug";

		public const string KiBarColor = "#4FC3F7";

		public const string PowerHudLabel = "BP:";

		public const string PowerHudColor = "#FFFFFF";

		public const string EnemyPowerLabel = "BP:";

		public const string EnemyPowerColor = "#FFFFFF";

		public const HudTextAlign EnemyPowerAlign = HudTextAlign.Center;

		public const float KaiokenAuraScaleOverForm = 1.3f;

		public const float KaiokenSkinTint = 0.4f;

		public const float KaiokenArmorGlow = 0.2f;

		private static readonly KiDiscConfig KienzanDisc = new KiDiscConfig
		{
			Radius = 1f,
			Thickness = 0.1f,
			Teeth = 50,
			ToothDepth = 0.1f,
			CoreWhiteness = 0.6f,
			Glow = 2.5f,
			Opacity = 0.85f,
			SpinSpeed = 1440f,
			LightIntensity = 2f,
			LightRange = 4f,
			HoldHeight = 0.2f
		};

		private static readonly OverheadPoseConfig KienzanPose = new OverheadPoseConfig
		{
			BlendSeconds = 0.25f,
			ArmHeight = 1f,
			ArmForward = -0.4f,
			ArmTwist = 0.5f,
			ForearmTwist = 0.5f,
			ElbowStretch = 1f,
			ShoulderLift = 1f,
			WristBend = 1f,
			HandOpen = 1f
		};

		public const int ScouterMetalAmount = 5;

		public const int ScouterColorAmount = 3;

		public const float ScouterInterferenceInterval = 0.06f;

		public static readonly Vector3 ScouterModelOffset = new Vector3(-0.0795f, 0.019f, 0f);

		public static readonly Vector3 ScouterModelRotation = new Vector3(-90f, -90f, 0f);

		public const float ScouterModelScale = 1f;

		public static readonly Vector3 ScouterIconRotation = new Vector3(-75.8f, 0f, -45f);

		public const float ScouterIconDistance = 0.8f;

		public const float PowerDisplayScale = 1f;

		public const string ChargeEffectPrefab = "Aura base";

		public const string ChargeSoundPrefab = "sfx_charred_mage_attack_charge";

		public const string ChargeEffectColor = "";

		public const float ChargeEffectScale = 1f;

		public const bool ChargeEffectForceLoop = true;

		public const string TransformEmote = "roar";

		public const string TransformAuraPrefab = "Aura base";

		public const float TransformAuraScale = 1.25f;

		public const float TransformAuraDuration = 2f;

		public const float TransformAuraFadeDuration = 1f;

		public const float TransformAuraFadeEndScale = 1f;

		public const bool TransformAuraForceLoop = false;

		public const float TransformAuraLightIntensity = 1f;

		public const string FormLightningPrefab = "fx_Lightning";

		public const float FormLightningInterval = 0.55f;

		public const float FormLightningIntervalJitter = 0.6f;

		public const int FormLightningCount = 1;

		public const float FormLightningScale = 0.5f;

		public const float FormLightningRadius = 0.6f;

		public const float FormLightningHeight = 1.1f;

		public const float FormLightningSpread = 1.8f;

		public const float FormLightningDuration = 0.35f;

		public const float FormLightningLightIntensity = 0f;

		public const float FormGlowIntensity = 0.8f;

		public const float FormGlowRange = 6f;

		public const float FormGlowHeight = 1.1f;

		public const float FormGlowPulseAmount = 0.15f;

		public const float FormGlowPulseSpeed = 0.3f;

		public const float FormGlowFade = 0.4f;

		public const bool FormGlowShadows = false;

		public static ConfigEntry<KeyboardShortcut> ToggleKiKey { get; private set; }

		public static ConfigEntry<bool> KiEnabledByDefault { get; private set; }

		public static ConfigEntry<KeyboardShortcut> ChargeKiKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> ToggleFlightKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> ToggleFlightAimKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> TransformKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> PowerDownKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> TransformStepUpKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> TransformStepDownKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> KaiokenKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> KaiokenOffKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> FireKiAttackKey { get; private set; }

		public static ConfigEntry<KeyboardShortcut> CycleKiAttackKey { get; private set; }

		public static ConfigEntry<bool> FlightTakeOffOnDoubleJump { get; private set; }

		public static ConfigEntry<float> FlightDoubleJumpWindow { get; private set; }

		public static ConfigEntry<float> MaxKi { get; private set; }

		public static ConfigEntry<float> MaxKiPerPowerLevel { get; private set; }

		public static ConfigEntry<float> KiRegenPerSecond { get; private set; }

		public static ConfigEntry<float> KiRegenFromPower { get; private set; }

		public static ConfigEntry<float> KiRegenDelay { get; private set; }

		public static ConfigEntry<float> KiRegenRestedMultiplier { get; private set; }

		public static ConfigEntry<float> KiRegenDelayRestedMultiplier { get; private set; }

		public static ConfigEntry<float> ChargeKiPerSecond { get; private set; }

		public static ConfigEntry<float> ChargeKiFromPower { get; private set; }

		public static ConfigEntry<bool> ChargeRequiresStandingStill { get; private set; }

		public static ConfigEntry<float> KiOnParryBarFraction { get; private set; }

		public static ConfigEntry<float> KiOnKillBarFraction { get; private set; }

		public static ConfigEntry<float> PunchKiCostPerDamage { get; private set; }

		public static ConfigEntry<float> KiCostPowerReduction { get; private set; }

		public static ConfigEntry<float> DefenseKiCostPowerReduction { get; private set; }

		public static ConfigEntry<float> CombatFormKiShare { get; private set; }

		public static ConfigEntry<float> MasteryFormCostReduction { get; private set; }

		public static ConfigEntry<float> PunchDamageFromPower { get; private set; }

		public static ConfigEntry<float> EnvironmentChopDamage { get; private set; }

		public static ConfigEntry<float> EnvironmentPickaxeDamage { get; private set; }

		public static ConfigEntry<float> ArmorBase { get; private set; }

		public static ConfigEntry<float> ArmorFractionWithoutKi { get; private set; }

		public static ConfigEntry<float> DamageTakenKiCost { get; private set; }

		public static ConfigEntry<float> ArmorFromPower { get; private set; }

		public static ConfigEntry<float> BlockPowerBase { get; private set; }

		public static ConfigEntry<float> BlockPowerFromPower { get; private set; }

		public static ConfigEntry<float> BlockKiCost { get; private set; }

		public static ConfigEntry<float> KiBarOffsetX { get; private set; }

		public static ConfigEntry<float> KiBarOffsetY { get; private set; }

		public static ConfigEntry<float> PowerHudOffsetX { get; private set; }

		public static ConfigEntry<float> PowerHudOffsetY { get; private set; }

		public static ConfigEntry<float> PowerHudFontSize { get; private set; }

		public static ConfigEntry<float> EnemyPowerOffsetX { get; private set; }

		public static ConfigEntry<float> EnemyPowerOffsetY { get; private set; }

		public static ConfigEntry<float> EnemyPowerFontSize { get; private set; }

		public static ConfigEntry<float> MasteryXpPerDamageDealt { get; private set; }

		public static ConfigEntry<float> MasteryXpPerDamageTaken { get; private set; }

		public static ConfigEntry<float> MasteryXpMaxPerEvent { get; private set; }

		public static ConfigEntry<float> MasteryXpCurveCompensation { get; private set; }

		public static ConfigEntry<float> MasteryXpPerBossBonus { get; private set; }

		public static ConfigEntry<float> MasteryXpBossMultiplierMax { get; private set; }

		public static TransformationConfig Ssj { get; private set; }

		public static TransformationConfig Ssj2 { get; private set; }

		public static TransformationConfig Ssj3 { get; private set; }

		public static TransformationConfig SsjGod { get; private set; }

		public static ConfigEntry<float> KaiokenStaminaCutPerLevel { get; private set; }

		public static ConfigEntry<float> KaiokenStaminaFloor { get; private set; }

		public static ConfigEntry<float> KaiokenHealthFloor { get; private set; }

		public static ConfigEntry<float> KaiokenExhaustionSeconds { get; private set; }

		public static ConfigEntry<float> KaiokenExhaustionSlow { get; private set; }

		public static ConfigEntry<float> KaiokenXpPerDamageDealt { get; private set; }

		public static ConfigEntry<float> KaiokenXpMaxPerEvent { get; private set; }

		public static ConfigEntry<float> KaiokenXpNegativeMarginBonus { get; private set; }

		public static ConfigEntry<float> KaiokenXpNegativeMarginFullAt { get; private set; }

		public static KaiokenTierConfig[] KaiokenTiers { get; private set; }

		public static KiAttackConfig KiBlast { get; private set; }

		public static KiAttackConfig Kamehameha { get; private set; }

		public static KiAttackConfig Kienzan { get; private set; }

		public static ConfigEntry<float> RuneFindChance { get; private set; }

		public static ConfigEntry<float> RuneFindChanceStep { get; private set; }

		public static ConfigEntry<float> RuneCooldownDays { get; private set; }

		public static ConfigEntry<float> FlightKiPerSecond { get; private set; }

		public static ConfigEntry<float> FlightFastKiMultiplier { get; private set; }

		public static ConfigEntry<float> FlightHoverKiMultiplier { get; private set; }

		public static ConfigEntry<float> FlightCombatHoverMultiplier { get; private set; }

		public static ConfigEntry<float> FlightCombatHoverRange { get; private set; }

		public static ConfigEntry<float> FlightBaseSpeed { get; private set; }

		public static ConfigEntry<float> FlightSpeedFromPower { get; private set; }

		public static ConfigEntry<float> FlightFormSpeedShare { get; private set; }

		public static ConfigEntry<float> FlightFastSpeedMultiplier { get; private set; }

		public static ConfigEntry<float> FlightVerticalSpeedFactor { get; private set; }

		public static ConfigEntry<float> FlightTurnSpeed { get; private set; }

		public static ConfigEntry<float> FlightAimDivePitch { get; private set; }

		public static ConfigEntry<float> FlightSpeedSkillBonus { get; private set; }

		public static ConfigEntry<float> FlightKiSkillReduction { get; private set; }

		public static ConfigEntry<float> FlightKiSkillCurve { get; private set; }

		public static ConfigEntry<float> FlightFallDamageSkillReduction { get; private set; }

		public static ConfigEntry<float> FlightKiPowerReduction { get; private set; }

		public static ConfigEntry<float> FlightXpPerMeter { get; private set; }

		public static ConfigEntry<float> FlightFormKiShare { get; private set; }

		public static ConfigEntry<float> FlightWeightPenalty { get; private set; }

		public static ConfigEntry<float> FlightWeightCurve { get; private set; }

		public static ConfigEntry<float> FlightMaxSpeed { get; private set; }

		public static ConfigEntry<float> FlightMovementDebuffStrength { get; private set; }

		public static ConfigEntry<float> PowerK1Health { get; private set; }

		public static ConfigEntry<float> PowerK2WeaponDamage { get; private set; }

		public static ConfigEntry<float> PowerK3Armor { get; private set; }

		public static ConfigEntry<float> PowerK4PowerSkill { get; private set; }

		public static ConfigEntry<float> PowerK5LateGame { get; private set; }

		public static ConfigEntry<float> PowerLateGameExponent { get; private set; }

		public static ConfigEntry<float> SkillXpPerDamageDealt { get; private set; }

		public static ConfigEntry<float> SkillXpPerDamageTaken { get; private set; }

		public static ConfigEntry<float> SkillXpWeightBonus { get; private set; }

		public static ConfigEntry<float> SkillXpMaxPerEvent { get; private set; }

		public static ConfigEntry<float> SkillXpWeaponFactor { get; private set; }

		public static ConfigEntry<float> RatingK1Health { get; private set; }

		public static ConfigEntry<float> RatingArmorScale { get; private set; }

		public static ConfigEntry<float> RatingK2Damage { get; private set; }

		public static ConfigEntry<float> RatingCreatureMaxAttackRate { get; private set; }

		public static ConfigEntry<float> RatingFormShare { get; private set; }

		public static ConfigEntry<bool> VerboseLogging { get; private set; }

		public static ConfigEntry<int> ConfigVersion { get; private set; }

		private static string KaiokenTierSection(int index, string name)
		{
			return $"3.5.{index + 1} - Kaioken {name}";
		}

		public static void Init(ConfigFile config)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_0060: 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_0085: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Expected O, but got Unknown
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Expected O, but got Unknown
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Expected O, but got Unknown
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Expected O, but got Unknown
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Expected O, but got Unknown
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Expected O, but got Unknown
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Expected O, but got Unknown
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Expected O, but got Unknown
			//IL_0352: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Expected O, but got Unknown
			//IL_039e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a8: Expected O, but got Unknown
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Expected O, but got Unknown
			//IL_0424: Unknown result type (might be due to invalid IL or missing references)
			//IL_042e: Expected O, but got Unknown
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_0471: Expected O, but got Unknown
			//IL_04aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b4: Expected O, but got Unknown
			//IL_04ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f7: Expected O, but got Unknown
			//IL_0530: Unknown result type (might be due to invalid IL or missing references)
			//IL_053a: Expected O, but got Unknown
			//IL_0573: Unknown result type (might be due to invalid IL or missing references)
			//IL_057d: Expected O, but got Unknown
			//IL_05b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c0: Expected O, but got Unknown
			//IL_05f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0603: Expected O, but got Unknown
			//IL_063c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0646: Expected O, but got Unknown
			//IL_066d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0677: Expected O, but got Unknown
			//IL_06b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ba: Expected O, but got Unknown
			//IL_06f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06fd: Expected O, but got Unknown
			//IL_0736: Unknown result type (might be due to invalid IL or missing references)
			//IL_0740: Expected O, but got Unknown
			//IL_0779: Unknown result type (might be due to invalid IL or missing references)
			//IL_0783: Expected O, but got Unknown
			//IL_07bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_07c6: Expected O, but got Unknown
			//IL_07ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0809: Expected O, but got Unknown
			//IL_0842: Unknown result type (might be due to invalid IL or missing references)
			//IL_084c: Expected O, but got Unknown
			//IL_0885: Unknown result type (might be due to invalid IL or missing references)
			//IL_088f: Expected O, but got Unknown
			//IL_08c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_08d2: Expected O, but got Unknown
			//IL_090b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0915: Expected O, but got Unknown
			//IL_094e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0958: Expected O, but got Unknown
			//IL_0991: Unknown result type (might be due to invalid IL or missing references)
			//IL_099b: Expected O, but got Unknown
			//IL_09d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_09de: Expected O, but got Unknown
			//IL_0a17: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a21: Expected O, but got Unknown
			//IL_0a5a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a64: Expected O, but got Unknown
			//IL_0a9d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aa7: Expected O, but got Unknown
			//IL_0ae0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aea: Expected O, but got Unknown
			//IL_0b23: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b2d: Expected O, but got Unknown
			//IL_0b66: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b70: Expected O, but got Unknown
			//IL_0ba9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bb3: Expected O, but got Unknown
			//IL_0bec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bf6: Expected O, but got Unknown
			//IL_0c2f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c39: Expected O, but got Unknown
			//IL_0c72: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c7c: Expected O, but got Unknown
			//IL_0cb5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0cbf: Expected O, but got Unknown
			//IL_0cf8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d02: Expected O, but got Unknown
			//IL_0d3b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d45: Expected O, but got Unknown
			//IL_0d7e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d88: Expected O, but got Unknown
			//IL_0dc1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0dcb: Expected O, but got Unknown
			//IL_0e04: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e0e: Expected O, but got Unknown
			//IL_0e47: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e51: Expected O, but got Unknown
			//IL_0e8a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e94: Expected O, but got Unknown
			//IL_10c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_10d3: Expected O, but got Unknown
			//IL_110c: Unknown result type (might be due to invalid IL or missing references)
			//IL_1116: Expected O, but got Unknown
			//IL_114f: Unknown result type (might be due to invalid IL or missing references)
			//IL_1159: Expected O, but got Unknown
			//IL_1192: Unknown result type (might be due to invalid IL or missing references)
			//IL_119c: Expected O, but got Unknown
			//IL_11d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_11df: Expected O, but got Unknown
			//IL_1218: Unknown result type (might be due to invalid IL or missing references)
			//IL_1222: Expected O, but got Unknown
			//IL_125b: Unknown result type (might be due to invalid IL or missing references)
			//IL_1265: Expected O, but got Unknown
			//IL_129e: Unknown result type (might be due to invalid IL or missing references)
			//IL_12a8: Expected O, but got Unknown
			//IL_12e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_12eb: Expected O, but got Unknown
			//IL_1462: Unknown result type (might be due to invalid IL or missing references)
			//IL_146c: Expected O, but got Unknown
			//IL_14a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_14af: Expected O, but got Unknown
			//IL_14e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_14f2: Expected O, but got Unknown
			//IL_178e: Unknown result type (might be due to invalid IL or missing references)
			//IL_1798: Expected O, but got Unknown
			//IL_17d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_17e0: Expected O, but got Unknown
			//IL_1837: Unknown result type (might be due to invalid IL or missing references)
			//IL_1841: Expected O, but got Unknown
			//IL_187a: Unknown result type (might be due to invalid IL or missing references)
			//IL_1884: Expected O, but got Unknown
			//IL_18bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_18c7: Expected O, but got Unknown
			//IL_1900: Unknown result type (might be due to invalid IL or missing references)
			//IL_190a: Expected O, but got Unknown
			//IL_1943: Unknown result type (might be due to invalid IL or missing references)
			//IL_194d: Expected O, but got Unknown
			//IL_1986: Unknown result type (might be due to invalid IL or missing references)
			//IL_1990: Expected O, but got Unknown
			//IL_19c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_19d3: Expected O, but got Unknown
			//IL_1a0c: Unknown result type (might be due to invalid IL or missing references)
			//IL_1a16: Expected O, but got Unknown
			//IL_1a4f: Unknown result type (might be due to invalid IL or missing references)
			//IL_1a59: Expected O, but got Unknown
			//IL_1a92: Unknown result type (might be due to invalid IL or missing references)
			//IL_1a9c: Expected O, but got Unknown
			//IL_1ad5: Unknown result type (might be due to invalid IL or missing references)
			//IL_1adf: Expected O, but got Unknown
			//IL_1b18: Unknown result type (might be due to invalid IL or missing references)
			//IL_1b22: Expected O, but got Unknown
			//IL_1b5b: Unknown result type (might be due to invalid IL or missing references)
			//IL_1b65: Expected O, but got Unknown
			//IL_1b9e: Unknown result type (might be due to invalid IL or missing references)
			//IL_1ba8: Expected O, but got Unknown
			//IL_1be1: Unknown result type (might be due to invalid IL or missing references)
			//IL_1beb: Expected O, but got Unknown
			//IL_1c24: Unknown result type (might be due to invalid IL or missing references)
			//IL_1c2e: Expected O, but got Unknown
			//IL_1c67: Unknown result type (might be due to invalid IL or missing references)
			//IL_1c71: Expected O, but got Unknown
			//IL_1caa: Unknown result type (might be due to invalid IL or missing references)
			//IL_1cb4: Expected O, but got Unknown
			//IL_1ced: Unknown result type (might be due to invalid IL or missing references)
			//IL_1cf7: Expected O, but got Unknown
			//IL_1d30: Unknown result type (might be due to invalid IL or missing references)
			//IL_1d3a: Expected O, but got Unknown
			//IL_1d73: Unknown result type (might be due to invalid IL or missing references)
			//IL_1d7d: Expected O, but got Unknown
			//IL_1db6: Unknown result type (might be due to invalid IL or missing references)
			//IL_1dc0: Expected O, but got Unknown
			//IL_1df9: Unknown result type (might be due to invalid IL or missing references)
			//IL_1e03: Expected O, but got Unknown
			//IL_1e3c: Unknown result type (might be due to invalid IL or missing references)
			//IL_1e46: Expected O, but got Unknown
			//IL_1e7f: Unknown result type (might be due to invalid IL or missing references)
			//IL_1e89: Expected O, but got Unknown
			//IL_1ec2: Unknown result type (might be due to invalid IL or missing references)
			//IL_1ecc: Expected O, but got Unknown
			//IL_1f05: Unknown result type (might be due to invalid IL or missing references)
			//IL_1f0f: Expected O, but got Unknown
			//IL_1f48: Unknown result type (might be due to invalid IL or missing references)
			//IL_1f52: Expected O, but got Unknown
			//IL_1f8b: Unknown result type (might be due to invalid IL or missing references)
			//IL_1f95: Expected O, but got Unknown
			//IL_1fce: Unknown result type (might be due to invalid IL or missing references)
			//IL_1fd8: Expected O, but got Unknown
			//IL_2011: Unknown result type (might be due to invalid IL or missing references)
			//IL_201b: Expected O, but got Unknown
			//IL_2054: Unknown result type (might be due to invalid IL or missing references)
			//IL_205e: Expected O, but got Unknown
			//IL_2097: Unknown result type (might be due to invalid IL or missing references)
			//IL_20a1: Expected O, but got Unknown
			//IL_20da: Unknown result type (might be due to invalid IL or missing references)
			//IL_20e4: Expected O, but got Unknown
			//IL_211d: Unknown result type (might be due to invalid IL or missing references)
			//IL_2127: Expected O, but got Unknown
			//IL_2160: Unknown result type (might be due to invalid IL or missing references)
			//IL_216a: Expected O, but got Unknown
			//IL_21a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_21ad: Expected O, but got Unknown
			//IL_21e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_21f0: Expected O, but got Unknown
			//IL_2229: Unknown result type (might be due to invalid IL or missing references)
			//IL_2233: Expected O, but got Unknown
			//IL_225a: Unknown result type (might be due to invalid IL or missing references)
			//IL_2264: Expected O, but got Unknown
			ConfigVersion = config.Bind<int>("9 - Debug", "ConfigVersion", 0, new ConfigDescription("Bookkeeping: which round of balance migrations this file has already received. Do not edit — lowering it makes the mod redo a migration and overwrite balance keys you may have tuned.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = false,
				Browsable = false,
				Order = 0
			} }));
			ToggleKiKey = config.Bind<KeyboardShortcut>("1 - General", "ToggleKiKey", new KeyboardShortcut((KeyCode)107, Array.Empty<KeyCode>()), new ConfigDescription("Key that toggles ki on and off. Ki turned off behaves like zero ki: no damage bonus, no mastery accumulating, no ki term in the battle power.", (AcceptableValueBase)null, new object[1] { ClientSide(100) }));
			KiEnabledByDefault = config.Bind<bool>("1 - General", "KiEnabledByDefault", true, new ConfigDescription("Starting ki state on a brand new character.", (AcceptableValueBase)null, new object[1] { ClientSide(90) }));
			ChargeKiKey = config.Bind<KeyboardShortcut>("1 - General", "ChargeKiKey", new KeyboardShortcut((KeyCode)114, Array.Empty<KeyCode>()), new ConfigDescription("Key HELD DOWN to actively charge ki, far faster than passive regeneration.", (AcceptableValueBase)null, new object[1] { ClientSide(95) }));
			ToggleFlightKey = config.Bind<KeyboardShortcut>("1 - General", "ToggleFlightKey", new KeyboardShortcut((KeyCode)102, Array.Empty<KeyCode>()), new ConfigDescription("Key that takes off and lands. Once airborne, movement is the usual one: the game's Jump button climbs, Crouch descends and Run flies fast.", (AcceptableValueBase)null, new object[1] { ClientSide(85) }));
			ToggleFlightAimKey = config.Bind<KeyboardShortcut>("1 - General", "ToggleFlightAimKey", new KeyboardShortcut((KeyCode)102, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), new ConfigDescription("Key that switches flight between classic and aim mode. Classic: forward is always level and only Jump and Crouch change altitude. Aim: moving forward flies where you are looking, up and down included, and moving back flies the opposite way; strafing stays level and Jump and Crouch still work on top. Aim is the default. Works on the ground or in the air, and the choice is saved with the character.", (AcceptableValueBase)null, new object[1] { ClientSide(77) }));
			TransformKey = config.Bind<KeyboardShortcut>("1 - General", "TransformKey", new KeyboardShortcut((KeyCode)116, Array.Empty<KeyCode>()), new ConfigDescription("Key that transforms straight into the HIGHEST form you have unlocked, skipping everything below it. Does nothing when you are already there.", (AcceptableValueBase)null, new object[1] { ClientSide(84) }));
			TransformStepUpKey = config.Bind<KeyboardShortcut>("1 - General", "TransformStepUpKey", new KeyboardShortcut((KeyCode)116, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), new ConfigDescription("Key that goes UP one step on the ladder: base form to SSJ, SSJ to SSJ2, and so on. Use it to stop at an intermediate form instead of jumping to the top. Does nothing at the top of what you have unlocked.", (AcceptableValueBase)null, new object[1] { ClientSide(83) }));
			PowerDownKey = config.Bind<KeyboardShortcut>("1 - General", "PowerDownKey", new KeyboardShortcut((KeyCode)122, Array.Empty<KeyCode>()), new ConfigDescription("Key that drops you straight back to base form, from whatever step you are on — no walking back down the ladder. Running out of ki does the same thing on its own: at zero there is nothing to hold any form with.", (AcceptableValueBase)null, new object[1] { ClientSide(82) }));
			TransformStepDownKey = config.Bind<KeyboardShortcut>("1 - General", "TransformStepDownKey", new KeyboardShortcut((KeyCode)122, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), new ConfigDescription("Key that goes DOWN one step, to trade power for a smaller ki drain without leaving the ladder entirely. From the first form it returns to base.", (AcceptableValueBase)null, new object[1] { ClientSide(81) }));
			KaiokenKey = config.Bind<KeyboardShortcut>("1 - General", "KaiokenKey", new KeyboardShortcut((KeyCode)113, Array.Empty<KeyCode>()), new ConfigDescription("Kaioken key. Press to turn it on at x2, and press again to go up one tier, up to the highest you have learned. Works on top of any form, base form included.", (AcceptableValueBase)null, new object[1] { ClientSide(80) }));
			KaiokenOffKey = config.Bind<KeyboardShortcut>("1 - General", "KaiokenOffKey", new KeyboardShortcut((KeyCode)113, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), new ConfigDescription("Key that turns Kaioken off, from any tier.", (AcceptableValueBase)null, new object[1] { ClientSide(79) }));
			FireKiAttackKey = config.Bind<KeyboardShortcut>("1 - General", "FireKiAttackKey", new KeyboardShortcut((KeyCode)118, Array.Empty<KeyCode>()), new ConfigDescription("Key that fires the selected ki attack, aimed where you are looking. Needs ki turned on, enough ki for the shot, and the attack unlocked. The ki is spent on the shot, hit or miss.", (AcceptableValueBase)null, new object[1] { ClientSide(79) }));
			CycleKiAttackKey = config.Bind<KeyboardShortcut>("1 - General", "CycleKiAttackKey", new KeyboardShortcut((KeyCode)118, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), new ConfigDescription("Key that cycles through the ki attacks you have unlocked. With a single one unlocked it just names it on screen. The selection is not saved: every session starts on the first attack.", (AcceptableValueBase)null, new object[1] { ClientSide(78) }));
			FlightTakeOffOnDoubleJump = config.Bind<bool>("1 - General", "TakeOffOnDoubleJump", true, new ConfigDescription("Tapping the Jump button twice quickly takes off, on the ground or mid-air. It only takes OFF, never lands: Jump is what climbs while flying, so a double tap up there would fight the control you are already using. ToggleFlightKey and touching the ground are what land you.", (AcceptableValueBase)null, new object[1] { ClientSide(80) }));
			FlightDoubleJumpWindow = config.Bind<float>("1 - General", "DoubleJumpWindow", 0.35f, new ConfigDescription("Maximum seconds between the two taps. Too high and normal jump spamming launches you by accident; too low and the double tap stops registering.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), new object[1] { ClientSide(75) }));
			MaxKi = config.Bind<float>("2 - Ki", "MaxKi", 50f, new ConfigDescription("Maximum ki at level 0 of the Power Level skill. The cap grows from here — see MaxKiPerPowerLevel. With a fixed cap the bar would be the same size from the first boss to the last and progression would never show up on the HUD. (Playtest value, 2026-07-31. Still being tuned.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 10000f), new object[1] { AdminOnly(100) }));
			MaxKiPerPowerLevel = config.Bind<float>("2 - Ki", "MaxKiPerPowerLevel", 3f, new ConfigDescription("Maximum ki added per level of Power Level. With the default, level 100 quadruples the bar (100 base + 300).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { AdminOnly(95) }));
			KiRegenPerSecond = config.Bind<float>("2 - Ki", "KiRegenPerSecond", 2f, new ConfigDescription("Ki regenerated per second while idle. Deliberately low: passive regeneration is the safety net, not the normal way to get ki back. If you want ki, you charge for it. (Playtest value, 2026-08-13. Went 0.5 → 1 on 2026-08-01, when taking damage started costing ki and the safety net had to hold more; 1 → 2 once flight got cheaper and the downtime between flights was the thing being waited on.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), new object[1] { AdminOnly(90) }));
			KiRegenFromPower = config.Bind<float>("2 - Ki", "KiRegenFromPower", 0.0075f, new ConfigDescription("Ki per second ADDED to the passive regeneration for each point of raw battle power. Exists because the bar grows with power (MaxKiPerPowerLevel) and a flat tap does not: without this, the stronger the character the SLOWER he fills his own bar, which is the opposite of the intent. The default keeps seconds-to-fill roughly flat across the whole game instead of making the strong player faster — the conservative half of the fix. Check it with saiya_ki, which prints seconds to fill.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { AdminOnly(85) }));
			KiRegenDelay = config.Bind<float>("2 - Ki", "KiRegenDelay", 5f, new ConfigDescription("Seconds without regenerating after spending ki. Deliberately long, together with a low KiRegenPerSecond: spending ki should hurt, and recovering it should be an action. (Calibrated in the 2026-07-28 playtest.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { AdminOnly(70) }));
			KiRegenRestedMultiplier = config.Bind<float>("2 - Ki", "KiRegenRestedMultiplier", 1.5f, new ConfigDescription("Multiplies passive ki regeneration while the player has the vanilla Rested buff (fire under shelter, or waking up in a bed). Same 1.5 the game itself uses for health and stamina regen, so the ki bar reads as one more thing Rested covers instead of a mod-only rule. Multiplicative, unlike the additive bonuses elsewhere in the mod, because it has to keep up with a tap that already grows with battle power. 1 disables it. Does NOT touch active charging: making charging faster at home would only say 'top up before leaving', which is not a decision worth having.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), new object[1] { AdminOnly(68) }));
			KiRegenDelayRestedMultiplier = config.Bind<float>("2 - Ki", "KiRegenDelayRestedMultiplier", 0.6f, new ConfigDescription("Multiplies KiRegenDelay while the player has the vanilla Rested buff. With the defaults, the 5s pause after spending ki becomes 3s. This is the half of the Rested bonus that actually gets noticed in combat — the multiplier on a small passive tap does not. 1 disables it; 0 removes the pause entirely while rested.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[1] { AdminOnly(66) }));
			ChargeKiPerSecond = config.Bind<float>("2 - Ki", "ChargeKiPerSecond", 5f, new ConfigDescription("Ki per second while the charge key is held. Should be much higher than KiRegenPerSecond — the point is that charging is a deliberate action worth taking, not just waiting faster. Deliberately ignores KiRegenDelay. (Playtest value, 2026-07-31. Still being tuned.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), new object[1] { AdminOnly(60) }));
			ChargeKiFromPower = config.Bind<float>("2 - Ki", "ChargeKiFromPower", 0.075f, new ConfigDescription("Ki per second ADDED to active charging for each point of raw battle power. Same reason as KiRegenFromPower: with a flat 5/s, filling the bar goes from 10 seconds early on to over a minute late, because only the cap grows. The number that matters when tuning this is NOT ki per second, it is seconds-to-fill — read it off saiya_ki at a low and a high skill level.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { AdminOnly(55) }));
			ChargeRequiresStandingStill = config.Bind<bool>("2 - Ki", "ChargeRequiresStandingStill", true, new ConfigDescription("If true, moving interrupts charging. Charging while standing still is the classic Dragon Ball gesture and creates a real choice: stopping to charge leaves you exposed. (Both tested in the 2026-07-28 playtest; standing still won.)", (AcceptableValueBase)null, new object[1] { AdminOnly(50) }));
			KiOnParryBarFraction = config.Bind<float>("2 - Ki", "KiOnParryBarFraction", 0.25f, new ConfigDescription("Ki gained on a successful parry (a block timed right), as a fraction of your full ki bar: 0.25 refills a quarter of it. Only with ki on, and only when the parry actually stopped damage. 0 disables it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(45) }));
			KiOnKillBarFraction = config.Bind<float>("2 - Ki", "KiOnKillBarFraction", 0.5f, new ConfigDescription("Ki gained for landing the killing blow on a creature, as a fraction of your full ki bar: 0.5 refills half of it. Any weapon or ki attack counts, as long as ki is on. Tamed creatures and players give nothing. 0 disables it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(40) }));
			PunchKiCostPerDamage = config.Bind<float>("2.1 - Combat", "PunchKiCostPerDamage", 7f, new ConfigDescription("Ki consumed per point of damage the battle power ADDED to the punch — the mirror of DamageTakenKiCost, which charges per point the ki armor absorbed. Both measure the service ki rendered, so the cost scales with the payoff instead of aging into irrelevance. The cost is therefore PunchDamageFromPower * battle power * this, and the vanilla unarmed base damage is free — ki did not provide it. Insufficient ki does NOT cancel the hit: the punch lands with raw vanilla damage, without the bonus. Missing costs nothing (the charge happens on the hit, not on the swing). Set to zero to disable the cost. (Playtest value, 2026-08-01: started at 1 to match DamageTakenKiCost and the punch was nearly free — the bar barely moved in a fight. 3 is what made the cost readable. Raised to 7 on 2026-09-20, when the ki cost of a form moved from per second held to per punch thrown.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { AdminOnly(100) }));
			DefenseKiCostPowerReduction = config.Bind<float>("2.1 - Combat", "DefenseKiCostPowerReduction", 0f, new ConfigDescription("Same hyperbolic discount as KiCostPowerReduction, but for the two DEFENSIVE ki costs: BlockKiCost and DamageTakenKiCost. 0 (the default) means no discount at all. Why it is separate and why it is zero: those two costs are charged per point of the ENEMY's hit that ki stopped, and an enemy's hit does not grow with your power — so there is no runaway growth here for a discount to correct. Sharing the punch's discount made defending cheaper exactly as it got stronger, and transforming multiplied the effect, because the form multiplies the power that buys the discount. Playtested 2026-09-20: in SSJ3 a block stopped 3.4x more damage for the same ki. Raise it only if late-game biomes hit hard enough to make defending unpayable — that is the one question this key is here to answer.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.02f), new object[1] { AdminOnly(59) }));
			CombatFormKiShare = config.Bind<float>("2.1 - Combat", "CombatFormKiShare", 0.2f, new ConfigDescription("How much of a transformation's power gain turns into EXTRA ki cost for the three combat costs — punching, blocking and taking hits: cost is multiplied by 1 + (PowerMultiplier - 1) * this * (1 - MasteryFormCostReduction * mastery/100). At 1 a form with x4 power costs 4x the ki per punch at mastery 0, which means the SAME damage per ki as base form — what the form buys there is the bigger hit, not efficiency. At the default 0.2 that same form costs 1.6x, so it already lands 2.5x the damage per ki before any mastery. Either way the surcharge is gone at mastery 100 (see MasteryFormCostReduction), where the whole multiplier becomes profit: at first you barely hold the form, in the end you wear it. Lower it to make transforming cheaper from the start; 0 gives the form's power away for free, which is how it behaved before 2026-09-20. Mirrors FormKiShare in the flight section. (Playtest value, 2026-09-21: shipped at 1 and fighting transformed drained the bar too fast; 0.5 was still too tight, and 0.2 is what made fighting in a form read as a reward instead of a bill. The base combat rates were not touched.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), new object[1] { AdminOnly(57) }));
			KiCostPowerReduction = config.Bind<float>("2.1 - Combat", "KiCostPowerReduction", 0.01f, new ConfigDescription("How much the combat battle power makes the three COMBAT ki costs cheaper — punching (PunchKiCostPerDamage), taking hits (DamageTakenKiCost) and blocking (BlockKiCost) — as 1 / (1 + this * combat power). 0 disables the discount and the costs stay strictly proportional to what ki delivered. Why it exists: all three costs come from the combat battle power, which has no ceiling — the late-game term grows forever and a transformation multiplies it — while the ki bar comes from the Power Level SKILL, which stops at 100. Without this, a punch eventually costs more than a full bar and lands with raw vanilla damage, and blocking drains the bar in two hits. \nThe shape matters: each cost approaches its own rate divided by this and never passes it, so actions per bar settles instead of falling to zero. For the punch at 0.01 that ceiling is 15 ki, so a full bar always buys a long fight no matter how far the battle power runs. \nIt also fixes transformations without a key of its own: a form multiplies the power, and it is the power that buys the discount, so the ratio of damage per bar between transformed and not approaches the form's PowerMultiplier instead of sitting at 1. \nOne key for all three deliberately: it is the same problem in all of them, and splitting it would invite cheap punches next to expensive blocks with no design question behind the difference. \nEarly game the power is small, so the discount is a few percent and the values calibrated on 2026-08-01 still hold there. (Playtest value, 2026-08-04. Started at 0.002, which was still tight enough that the transformed fight lived on the edge of the bar; 0.01 is what made the combat read as combat instead of bar management.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(95) }));
			MasteryFormCostReduction = config.Bind<float>("2.1 - Combat", "MasteryFormCostReduction", 1f, new ConfigDescription("How much of the extra ki cost that a transformation charges is paid back by that form's mastery. The form's surcharge is 1 + (PowerMultiplier - 1) * CombatFormKiShare * (1 - this * mastery/100), and it multiplies all four ki costs of fighting: punching, blocking, taking hits and ki attacks. 0 means mastery pays back nothing and the form charges its full surcharge forever. \n1 is the meaningful point: at mastery 100 the form costs EXACTLY what the base form costs for the same action, while still landing PowerMultiplier times the damage. Mastering a form stops it from charging extra to fight in, and that is when the multiplier becomes pure profit. \nWhy it is written against the multiplier and not as a flat rate per mastery level: the premium a form charges IS its multiplier, so the payback has to scale with it. A flat rate would need one number for a x2 form and another for a x3 one. This way every rung, including ones that do not exist yet, lands on its own base cost at mastery 100 with no retuning. \nIt reads the mastery of the ACTIVE form, which starts at zero on every new rung — so the rung you have mastered is cheaper to fight in than the one you just unlocked, which is the whole point. \nNote this gives mastery a SECOND payoff next to the drain reduction. It stays on the economy axis, not the power axis, so a form still never hits harder for being trained. \n(Rewritten on 2026-09-20: it used to be a term added inside the power discount's divisor, and that discount also read the form-multiplied power, so the form's premium mostly cancelled itself out — in SSJ3 a punch landed 4x the damage for 1.28x the cost. The premium is now an explicit multiplier and the power discount reads the BASE form's power.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(94) }));
			PunchDamageFromPower = config.Bind<float>("2.1 - Combat", "PunchDamageFromPower", 0.05f, new ConfigDescription("Fraction of the battle power ADDED to punch damage. Additive, not multiplicative: enemy HP grows roughly linearly across biomes, and an additive stat scales predictably against that. (Playtest value, 2026-08-01. Cut to a third of the 0.15 used on 2026-07-31 — the punch was outscaling the biomes.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { AdminOnly(90) }));
			EnvironmentChopDamage = config.Bind<float>("2.2 - Environment", "ChopDamage", 8f, new ConfigDescription("Chop damage a punch deals to a tree you have JUST become strong enough to damage. Twice that strength deals twice this, so a newly unlocked tree always takes the same number of punches, whatever the tier, and old ones fall fast. Reference: a birch has 80 HP, an oak 200, the bronze axe deals 40. Ki attacks use the same formula, weighted by how many punches one projectile is worth against an enemy. The punch costs the same ki it costs against an enemy, and with the bar empty it does nothing here. (Starting value, 2026-09-27. Not playtested yet.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), new object[1] { AdminOnly(100) }));
			EnvironmentPickaxeDamage = config.Bind<float>("2.2 - Environment", "PickaxeDamage", 6f, new ConfigDescription("Same as ChopDamage, for rocks and ore veins. Reference: a chunk of copper vein has 50 HP, the antler pickaxe deals 18. (Starting value, 2026-09-27. Not playtested yet.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), new object[1] { AdminOnly(99) }));
			ArmorBase = config.Bind<float>("2.1 - Combat", "ArmorBase", 1f, new ConfigDescription("Armor guaranteed while ki is on, before the share that comes from power. It exists so the player does not end up MORE fragile by turning ki on early in the game, when the skill is still at a low level. (Playtest value, 2026-07-31. Still being tuned.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), new object[1] { AdminOnly(80) }));
			ArmorFromPower = config.Bind<float>("2.1 - Combat", "ArmorFromPower", 0.06f, new ConfigDescription("Fraction of the battle power converted into armor. While ki is on this armor REPLACES equipment armor — worn pieces stop counting. Turning ki off gives vanilla armor back immediately. Reads the COMBAT battle power, so it grows with K5_LateGameBonus — that is why this is much lower than it looks like it should be. (Playtest value, 2026-08-01. Lowered from 0.15 when the late-game term doubled power at level 100 and armor came along for the ride.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { AdminOnly(70) }));
			ArmorFractionWithoutKi = config.Bind<float>("2.1 - Combat", "ArmorFractionWithoutKi", 0f, new ConfigDescription("Fraction of the ki armor that survives when the ki bar hits ZERO (the toggle is still on, the bar is just empty). At 0 an empty bar means no armor at all, since ki armor replaces equipment armor. Raise it if running out of ki mid fight turns into an unrecoverable death. It is a cliff, not a fade: armor is at full value down to the last point of ki and only drops at zero. Deliberate — ki swings hard during a fight (punches, flight), and losing armor for ATTACKING would punish the player for something that has nothing to do with being hit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(65) }));
			DamageTakenKiCost = config.Bind<float>("2.1 - Combat", "DamageTakenKiCost", 0.6f, new ConfigDescription("Ki consumed per point of damage the ki armor ABSORBED. Taking a hit costs ki the same way landing one does — the ki armor is sustained, not free. Damage that armor does not touch (poison, fall, drowning) absorbs nothing and therefore costs nothing. Set to zero to disable. Note the cost per hit is naturally capped near your armor value: armor can never absorb more than it is worth, so a huge hit does not drain the bar. (Playtest value, 2026-08-01. The conservative 0.15 it shipped with the same day was barely noticeable; at 1 a blocked point of damage costs a point of ki. Raised to 1.5 on 2026-09-20, when the cost of a form moved from per second held to per action taken, and cut to 0.6 the same day, when the defensive costs stopped taking the punch's power discount — see DefenseKiCostPowerReduction.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { AdminOnly(60) }));
			BlockPowerBase = config.Bind<float>("2.1 - Combat", "BlockPowerBase", 2f, new ConfigDescription("Block power guaranteed while ki is on, before the share that comes from power. The 2 is the vanilla unarmed value, kept as a floor so turning ki on at battle power zero never makes blocking WORSE than vanilla. Same role ArmorBase plays for armor, with one difference: unlike armor, this SURVIVES an empty ki bar (ArmorFractionWithoutKi only scales the power-derived share). Zero armor is a legal value; zero block power is a division by zero inside Humanoid.BlockAttack that turns your stamina into NaN permanently. Leave this above zero.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), new object[1] { AdminOnly(58) }));
			BlockPowerFromPower = config.Bind<float>("2.1 - Combat", "BlockPowerFromPower", 0.04f, new ConfigDescription("Fraction of the battle power converted into block power. While ki is on this REPLACES the blocker item's value — holding a shield changes nothing, exactly like ArmorFromPower replaces equipment armor. Turning ki off gives the shield back immediately. Kept well BELOW the vanilla shield ladder — under the wood shield across the whole progression. It has to be: a blocked hit runs through Valheim's damage/(4*armor) curve TWICE, once against this and again against the ki armor, so what looks like a modest number compounds into near-immunity. Run 'saiya_block <damage>' to see what a given hit actually does. (Playtest value, 2026-08-23. Cut from the 0.22 of 2026-08-01, which made an endgame block absorb ~86% of a hit for 2% of the ki bar.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { AdminOnly(56) }));
			BlockKiCost = config.Bind<float>("2.1 - Combat", "BlockKiCost", 0.3f, new ConfigDescription("Ki consumed per point of damage the ki BLOCK stopped, measured (not estimated) from the hit before and after Humanoid.BlockAttack. Same rule as the armor: if it stopped damage, it costs ki. A failed block stops nothing and costs nothing. Unlike the punch, an empty bar does not cancel anything — the block already happened when the charge lands, so it drains what is there, like the armor does. This is the most expensive thing in the mod by design: blocking stops far more damage than armor absorbs, so it should be a beam, not a stance. Lower it if holding block for two hits empties the bar. Set to zero to make blocking free. (2026-08-01: 0.5. Raised to 1.2 on 2026-09-20 with the rest of the combat ki costs, then cut to 0.3 the same day: the first playtest of the rework found blocking nearly free while transformed, the defensive costs stopped taking the punch's power discount, and the rate had been inflated to compensate for it.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { AdminOnly(54) }));
			KiBarOffsetX = config.Bind<float>("7 - HUD", "KiBarOffsetX", 0f, new ConfigDescription("Horizontal offset of the ki bar, in pixels. Applies live: edit the file with the game running and the bar moves.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-500f, 500f), new object[1] { ClientSide(90) }));
			KiBarOffsetY = config.Bind<float>("7 - HUD", "KiBarOffsetY", -50f, new ConfigDescription("Vertical offset of the ki bar, in pixels, relative to the position of the native bars. Negative moves down. Applies live. Do not raise this much: at -30 the ki bar lands on top of the stamina bar. (Playtest value, 2026-07-31.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-500f, 500f), new object[1] { ClientSide(80) }));
			PowerHudOffsetX = config.Bind<float>("7 - HUD", "PowerHudOffsetX", 0f, new ConfigDescription("Horizontal offset of the text, in pixels, RELATIVE to the minimap's biome label. Positive moves right. Zero puts it exactly on top of that label, so this key and the Y one below are what actually place it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-500f, 500f), new object[1] { ClientSide(48) }));
			PowerHudOffsetY = config.Bind<float>("7 - HUD", "PowerHudOffsetY", -200f, new ConfigDescription("Vertical offset of the text, in pixels, relative to the biome label. Negative moves down. (Playtest value, 2026-09-05. The first guess was -28, one line below the label; on screen the minimap needs a lot more clearance than that. Its layout is Unity asset data and cannot be read from code, so the number could only come from looking.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-500f, 500f), new object[1] { ClientSide(46) }));
			PowerHudFontSize = config.Bind<float>("7 - HUD", "PowerHudFontSize", 20f, new ConfigDescription("Font size, in canvas units — the same units the game's own UI uses, so it already follows resolution and UI scale. The clone has TMP auto-sizing turned off, which is what makes this key work at all: the biome label it is cloned from recomputes its own size every layout pass and would overwrite this. (Playtest value, 2026-09-05.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 60f), new object[1] { ClientSide(44) }));
			EnemyPowerOffsetX = config.Bind<float>("7 - HUD", "EnemyPowerOffsetX", 0f, new ConfigDescription("Horizontal offset of the text, in pixels, RELATIVE to the enemy's name label. Positive moves right. Zero keeps it centred like the name.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-300f, 300f), new object[1] { ClientSide(39) }));
			EnemyPowerOffsetY = config.Bind<float>("7 - HUD", "EnemyPowerOffsetY", -30f, new ConfigDescription("Vertical offset of the text, in pixels, relative to the enemy's name label. Negative moves down. The name sits ABOVE the health bar, so this has to clear the bar's height to land under it. (Playtest value, 2026-09-06. The -34 first guess sat a touch low; -30 tucks the number right under the bar. The hud's layout is Unity asset data and cannot be read from code, so this had to be found on screen.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-300f, 300f), new object[1] { ClientSide(38) }));
			EnemyPowerFontSize = config.Bind<float>("7 - HUD", "EnemyPowerFontSize", 16f, new ConfigDescription("Font size, in canvas units. Smaller than the player's own number on purpose: this one is drawn in the world, over the enemy, and several can be on screen at once. TMP auto-sizing is turned off on the clone, which is what makes this key work at all. (Playtest value, 2026-09-06. Raised from 14: at that size the number was unreadable at the distance you actually scan an enemy from.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 40f), new object[1] { ClientSide(37) }));
			MasteryXpPerDamageDealt = config.Bind<float>("3 - Transformations", "MasteryXpPerDamageDealt", 0.0045f, new ConfigDescription("Mastery XP per point of damage DEALT while wearing a form. Every form uses the same rate. Fighting inside the form is the only way to train it: holding it while standing still, flying or exploring pays nothing. The damage counted is what the target actually lost, so overkill on a weak creature does not pay, and weapon hits pay by XpWeaponFactor like Power Level does — the form trains the mod's way of fighting. A single hit pays every form up to the active one, each at its own level. This rate and MasteryXpCurveCompensation go together: raising the curve compensation also speeds up the first levels, so the rate has to come down with it. (0.0125 until 2026-09-28, cut to 0.0045 when the curve compensation arrived, so the first levels cost the same as before.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), new object[1] { AdminOnly(70) }));
			MasteryXpPerDamageTaken = config.Bind<float>("3 - Transformations", "MasteryXpPerDamageTaken", 0.0045f, new ConfigDescription("Mastery XP per point of damage TAKEN while wearing a form. Counted after armor and resistances, so taking hits from something weak is worth almost nothing. Same rate as damage dealt by default: holding the form through a beating is training too.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), new object[1] { AdminOnly(69) }));
			MasteryXpMaxPerEvent = config.Bind<float>("3 - Transformations", "MasteryXpMaxPerEvent", 0.45f, new ConfigDescription("Safety clamp: the most mastery XP a single hit can pay, dealt or taken, before the curve compensation and the boss multiplier. Stops one boss-sized hit from jumping several levels at once. At the default rate it only bites above 100 damage in one hit. (Cut from 50 to 25 to 2.5 to 1.25 to 0.45, each time together with the rate, so it still bites at the same damage.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1000f), new object[1] { AdminOnly(68) }));
			MasteryXpCurveCompensation = config.Bind<float>("3 - Transformations", "MasteryXpCurveCompensation", 0.7f, new ConfigDescription("How much of Valheim's rising XP curve mastery gives back. Each hit's XP is multiplied by (XP the form's next level costs) ^ this, so higher levels pay more per hit. 0 turns it off: every level costs Valheim's full curve (level 99 about 60 times the hits of level 5). 1 makes every level cost the same number of hits. The default 0.7 leans toward flat: level 99 costs about three times the hits of level 5. Changing this also changes how fast the FIRST levels go, so adjust MasteryXpPerDamageDealt and MasteryXpPerDamageTaken with it. (Added 2026-09-28: mastery was fine early and crawled from the middle on. 0.5 on paper, 0.8 after the first playtest, 0.7 on 2026-09-30.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(67) }));
			MasteryXpPerBossBonus = config.Bind<float>("3 - Transformations", "MasteryXpPerBossBonus", 3f, new ConfigDescription("How much a form's mastery XP speeds up for each boss defeated AFTER the one that unlocked it. The multiplier is 1 + this * (bosses defeated - the form's rung), floored at 1 and capped by MasteryXpBossMultiplierMax. 0 disables it. \nWith the defaults a form pays x1 while its own boss is the newest kill, x4 after the next boss falls and x7 from the one after that. \nWhat it is for: the form unlocked first is always the one furthest up the expensive end of the XP curve, and it crawls exactly when a stronger form has just made it look useless. This makes the older rung train faster the further the world has moved past it, which is also the reading that makes sense in fiction: the form is trivial to you now. It multiplies on top of MasteryXpCurveCompensation. \nIt reads the world's global keys, so a server syncs it for free and someone joining late arrives with whatever the group has already killed — the same rule the unlock gate itself follows. \nOnly the five classic bosses count. A form tied to a key this build does not know, which today means the Queen and the Fader, gets no bonus at all rather than a wrong one. \n(Raised from 2 on 2026-09-28, with the cap going from 4 to 6.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { AdminOnly(66) }));
			MasteryXpBossMultiplierMax = config.Bind<float>("3 - Transformations", "MasteryXpBossMultiplierMax", 7f, new ConfigDescription("Ceiling for the boss XP multiplier from MasteryXpPerBossBonus. The final multiplier is min(this, 1 + bonus * (bosses defeated - the form's rung)), floored at 1. 1 disables the boss bonus entirely. (Raised from 2 on 2026-09-21, together with the mastery XP cut: at a twentieth of the old rate a ceiling of 2 stopped the older form from catching up almost as soon as the bonus started paying. Raised again to 6 on 2026-09-28, with the step going from 2 to 3, and to 7 on 2026-09-30.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), new object[1] { AdminOnly(65) }));
			Ssj = BindTransformation(config, "3.1 - SSJ", 2f, 1f, 0.5f, 0f, 100f, "#FFE14A", "defeated_eikthyr", lightning: false, "", 1f, 1f, "", "Spiked", null, null, null, 1f, null, 20f, 30f);
			Ssj2 = BindTransformation(config, "3.2 - SSJ2", 3f, 2f, 0f, 0.2f, 200f, "#FFF08A", "defeated_gdking", lightning: true, "#66D9FF", 1f, 1.5f, "", "Spiked", null, null, null, 1f, null, 50f, 60f);
			Ssj3 = BindTransformation(config, "3.3 - SSJ3", 4f, 3f, 0f, 0.5f, 300f, "#FFE066", "defeated_bonemass", lightning: true, "#FFFFFF", 1f, 2f, "", "SaiyaHair6", null, null, null, 1f, null, 70f, 90f);
			SsjGod = BindTransformation(config, "3.4 - SSJ God", 3.5f, 2f, 0.5f, 0f, 350f, "#E8303A", "defeated_dragon", lightning: false, "", 1f, 2.5f, "", "Spiked", 3f, true, 1f, 0.5f, (DamageModifier)7, 40f, 50f);
			KaiokenStaminaCutPerLevel = config.Bind<float>("3.5 - Kaioken", "StaminaCutPerLevel", 0.04f, new ConfigDescription("Fraction of a tier's stamina drain removed per Kaioken skill level ABOVE the safe level: stamina/s = StaminaPerSecond x max(StaminaFloor, 1 - this x margin). With 0.04 and a floor of 0.2, every tier reaches the floor 20 levels past its safe level. Below the safe level the drain is the full base. Stamina does not regenerate while Kaioken is on, so this is the net cost.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.2f), new object[1] { AdminOnly(100) }));
			KaiokenStaminaFloor = config.Bind<float>("3.5 - Kaioken", "StaminaFloor", 0.2f, new ConfigDescription("The lowest the stamina drain can go, as a fraction of the tier's base. Never zero by design: a Kaioken that costs nothing would just stay on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), new object[1] { AdminOnly(99) }));
			KaiokenHealthFloor = config.Bind<float>("3.5 - Kaioken", "HealthFloor", 0.2f, new ConfigDescription("Kaioken turns itself off when health falls below this fraction of max health. The health drain never kills — this is the safety net, not a cost.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.9f), new object[1] { AdminOnly(90) }));
			KaiokenExhaustionSeconds = config.Bind<float>("3.5 - Kaioken", "ExhaustionSeconds", 10f, new ConfigDescription("Running out of stamina with Kaioken on turns it off and leaves you Exhausted for this many seconds: no stamina regeneration, slower movement and no Kaioken until it ends. 0 turns Exhaustion off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), new object[1] { AdminOnly(80) }));
			KaiokenExhaustionSlow = config.Bind<float>("3.5 - Kaioken", "ExhaustionSlow", 0.3f, new ConfigDescription("Fraction of movement speed lost while Exhausted. 0.3 = 70% speed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.9f), new object[1] { AdminOnly(79) }));
			KaiokenXpPerDamageDealt = config.Bind<float>("3.5 - Kaioken", "XpPerDamageDealt", 0.07f, new ConfigDescription("Kaioken skill XP per point of damage dealt with Kaioken on, capped at the target's remaining HP like Power Level. Same starting rate as Power Level: the skill is meant to be as slow. Only damage dealt trains it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { AdminOnly(70) }));
			KaiokenXpMaxPerEvent = config.Bind<float>("3.5 - Kaioken", "XpMaxPerEvent", 5f, new ConfigDescription("Safety clamp: maximum Kaioken XP from a single hit, before the negative margin bonus.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { AdminOnly(69) }));
			KaiokenXpNegativeMarginBonus = config.Bind<float>("3.5 - Kaioken", "XpNegativeMarginBonus", 1f, new ConfigDescription("Extra Kaioken XP, as a fraction, for hits landed while the tier is BEYOND your limit (below its safe level, health draining). Grows with how far below you are, and is whole at XpNegativeMarginFullAt levels below. 1 = up to double the XP. Training past the limit pays more.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { AdminOnly(68) }));
			KaiokenXpNegativeMarginFullAt = config.Bind<float>("3.5 - Kaioken", "XpNegativeMarginFullAt", 20f, new ConfigDescription("How many levels below the tier's safe level the negative margin XP bonus reaches its full value. Closer than this pays a proportional part of it; further pays no more.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), new object[1] { AdminOnly(67) }));
			KaiokenTiers = new KaiokenTierConfig[5]
			{
				BindKaiokenTier(config, 0, "x2", 1.2f, 10f, 4f, 0.15f, 0.3f, 0.2f, 0.1f, (Biome)1, "#FF4530", 2f),
				BindKaiokenTier(config, 1, "x3", 1.4f, 20f, 6f, 0.25f, 0.5f, 0.3f, 0.15f, (Biome)8, "#FF3821", 2.5f),
				BindKaiokenTier(config, 2, "x4", 1.6f, 30f, 8f, 0.35f, 0.7f, 0.4f, 0.2f, (Biome)2, "#FF311A", 3f),
				BindKaiokenTier(config, 3, "x10", 2f, 60f, 11f, 0.5f, 1f, 0.7f, 0.3f, (Biome)16, "#FF1B0F", 4f),
				BindKaiokenTier(config, 4, "x20", 2.5f, 80f, 14f, 0.7f, 1.5f, 1f, 0.4f, (Biome)32, "#FF0000", 5f)
			};
			RuneFindChance = config.Bind<float>("4 - Runestones", "RuneFindChance", 0.25f, new ConfigDescription("Chance that reading a lore runestone teaches a ki attack, when that biome still has one you do not know. Runestones that teach nothing make the next one in the same biome likelier — see RuneFindChanceStep. (Starting value. Not playtested yet.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(100) }));
			RuneFindChanceStep = config.Bind<float>("4 - Runestones", "RuneFindChanceStep", 0.25f, new ConfigDescription("Added to RuneFindChance for every runestone in the same biome that taught nothing, so bad luck cannot last forever: at 0.25 + 0.25, the fourth empty stone in a row always teaches. Resets when the biome teaches something. Counted per character and per biome. (Starting value. Not playtested yet.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(90) }));
			RuneCooldownDays = config.Bind<float>("4 - Runestones", "RuneCooldownDays", 3f, new ConfigDescription("In-game days before the same runestone can teach the same character again (one day is 20 real minutes; sleeping skips ahead). Until then, reading it only shows the vanilla text. Long on purpose: short would mean camping next to the stone at your base, and runestones are there to make you explore. (Starting value. Not playtested yet.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), new object[1] { AdminOnly(80) }));
			KiBlast = BindKiAttack(config, "4.1 - Ki Blast", 9f, 0.036f, 20f, 0.5f, "GoblinShaman_projectile_fireball", "", "smoke, fire", "", "#FFFF00", (Biome)1, "", 1, 0.05f, 30f, 2f, 30f, 3f, 1f, 0f, 0.15f, 0.4f, "", "", 1f, 1f, 0.35f, "", "", 1f, chargeFullEffectLoop: false, chargeFullEffectReplaces: true, 0f, EffectAnchor.RightHand, "", "", 1f, "", 1.2f);
			Kamehameha = BindKiAttack(config, "4.2 - Kamehameha", 1f, 0.008f, 2f, 2f, "projectile_beam", "fx_shaman_fireball_expl", "smoke, fire", "", "#66CCFF", (Biome)8, "", 60, 0.03f, 3f, 0f, 50f, 5f, 2f, 4f, 0.15f, 0.2f, "", "", 1f, 0f, 0f, "vfx_blocked", "", 1f, chargeFullEffectLoop: false, chargeFullEffectReplaces: true, 0.13f, EffectAnchor.RightHand, "GoblinShaman_projectile_fireball", "", 1f, "flames_world", 1.5f);
			Kienzan = BindKiAttack(config, "4.3 - Kienzan", 10f, 0.1f, 40f, 0.5f, "GoblinShaman_projectile_fireball", "", "smoke, fire", "", "#FFE14D", (Biome)2, "", 1, 0.05f, 30f, 0f, 25f, 3f, 1f, 2.5f, 0.1f, 0.2f, "", "", 1f, 0f, 0f, "vfx_blocked", "", 1f, chargeFullEffectLoop: false, chargeFullEffectReplaces: false, 0f, EffectAnchor.RightHand, "", "", 1f, "", 2f, shotShape: false);
			Kienzan.ChargePose = ChargePose.OverheadDisc;
			Kienzan.Pierce = true;
			Kienzan.DamageType = KiDamageType.Pierce;
			Kienzan.HoldStillWhileCharging = false;
			Kienzan.ChargeMinDamage = config.Bind<float>("4.3 - Kienzan", "ChargeMinDamage", 0.3f, new ConfigDescription("Fraction of the full damage the disc deals when thrown at the smallest charge that still fires (MinChargeRatio). It rises in a straight line to full damage at a full charge. 1 makes the charge not matter for damage. (Added 2026-09-30: the ki is paid while charging, and a short charge used to throw a full-damage disc for a fraction of the cost.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(78) }));
			Kienzan.HitRadius = config.Bind<float>("4.3 - Kienzan", "HitRadius", 1f, new ConfigDescription("Radius in metres of the sphere the disc uses to find what it cuts, at a full charge. It grows with the disc, so a shorter charge hits a smaller area. Wider hits more of a crowd, but it also clips the ground sooner when thrown low — and the ground is the only thing that stops the disc. (Raised from 0.4 on 2026-09-30, after playtest.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 3f), new object[1] { AdminOnly(77) }));
			Kienzan.Disc = KienzanDisc;
			Kienzan.OverheadPose = KienzanPose;
			FlightKiPerSecond = config.Bind<float>("5 - Flight", "KiPerSecond", 3.5f, new ConfigDescription("Ki per second while flying. Flight should be a tool, not the default way to get around — otherwise the game's hostile terrain turns into scenery. Running out of ki in the air drops you. (Playtest value, 2026-08-13. Went 4 → 15 on 2026-07-31, because at 4 flight was cheap enough to become the default way to travel, then 15 → 10 while playing the ki attack stage, then 10 → 5: with BaseSpeed at 2 the early-game flight is slow enough to hold itself back without the ki cost doing it too. 5 → 3.5 on 2026-09-20: at 5 the level-0 bar bought ten seconds of flight, and players answered that by grinding the skill instead of playing.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { AdminOnly(100) }));
			FlightFastKiMultiplier = config.Bind<float>("5 - Flight", "FastKiMultiplier", 2f, new ConfigDescription("Ki cost multiplier while the Run button is held. (Playtest value, 2026-08-13. Was 2.5, above FastSpeedMultiplier on purpose so fast flight cost more per travelled metre; now equal to it, which makes the metre cost the same fast or slow and turns the Run button into a pure time-saver. Raise it above FastSpeedMultiplier to get the penalty back.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), new object[1] { AdminOnly(95) }));
			FlightHoverKiMultiplier = config.Bind<float>("5 - Flight", "HoverKiMultiplier", 1f, new ConfigDescription("Ki cost multiplier while hovering — airborne with no movement input at all, not even rising or descending. Below 1 holding position is cheaper than travelling, which is how this shipped until 2026-09-20; it was raised to 1 because hovering is exactly the posture used to cheese bosses from out of reach, and the discount was paying for it. See CombatHoverMultiplier for the part that only charges when something is actually fighting you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(94) }));
			FlightCombatHoverMultiplier = config.Bind<float>("5 - Flight", "CombatHoverMultiplier", 3f, new ConfigDescription("Extra ki cost multiplier for HOVERING while something hostile is alerted nearby — hanging in the air out of reach while a boss or a pack cannot touch you. Stacks on top of HoverKiMultiplier. Flying in combat is NOT affected: diving, circling and pulling out is air combat, which is the point of the mod. Only holding still is. 1 turns it off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), new object[1] { AdminOnly(93) }));
			FlightCombatHoverRange = config.Bind<float>("5 - Flight", "CombatHoverRange", 30f, new ConfigDescription("How far, in meters, an alerted hostile counts for CombatHoverMultiplier. Measured in 3D from your body, so something 30 m below you still counts — which is the whole point, since that is where it is when you are out of its reach. Tamed creatures and other players never count.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), new object[1] { AdminOnly(92) }));
			FlightBaseSpeed = config.Bind<float>("5 - Flight", "BaseSpeed", 2f, new ConfigDescription("Flight speed floor: skill 0, battle power 0, carrying nothing. Everything else is added or multiplied on top of this. CAREFUL: the flight skill bonus MULTIPLIES this floor, so lowering it also shrinks what levelling the skill is worth — at 2, a hundred levels of Flight buy +1 m/s on a fresh character. That is intended here: almost all of the speed is meant to come from SpeedFromPower. (Playtest value, 2026-07-31. Started at 15, went to 10, landed on 2.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), new object[1] { AdminOnly(90) }));
			FlightSpeedFromPower = config.Bind<float>("5 - Flight", "SpeedFromPower", 0.015f, new ConfigDescription("Speed ADDED per point of raw battle power. With ki on, power is k1*HP + k4*PowerLevel — so eating better and fighting more both make you fly faster, which is the Dragon Ball reading of getting stronger. Additive, like punch damage: enemy scaling is roughly linear across biomes and an additive stat tracks that predictably. Run saiya_fly to see how much this is contributing right now.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(88) }));
			FlightFormSpeedShare = config.Bind<float>("5 - Flight", "FormSpeedShare", 0.3f, new ConfigDescription("How much of a transformation's power gain turns into flight speed: the form multiplies speed by 1 + (PowerMultiplier - 1) * this. At 0.3, SSJ's x2 power is x1.3 speed and SSJ2's x3 is x1.6. Transformations are meant to be felt in combat first; letting the raw PowerMultiplier through sent flight straight into MaxSpeed, where extra power buys no actual velocity and SSJ and SSJ2 stop being distinguishable in the air. 1 restores the old behaviour (form speed = PowerMultiplier), 0 makes forms not affect flight speed at all. (Playtest value, 2026-08-21: flying transformed felt too fast while base form felt right.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(86) }));
			FlightFastSpeedMultiplier = config.Bind<float>("5 - Flight", "FastSpeedMultiplier", 2f, new ConfigDescription("Speed multiplier while the Run button is held. Vanilla already reads that button inside its own flight code, so fast flight costs no extra keybind. (Playtest value, 2026-08-13. Raised from 1.8.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), new object[1] { AdminOnly(85) }));
			FlightVerticalSpeedFactor = config.Bind<float>("5 - Flight", "VerticalSpeedFactor", 1f, new ConfigDescription("Climb and dive speed, as a fraction of the horizontal speed. 1.0 goes up as fast as forward, so altitude costs no more than distance. (Playtest value, 2026-08-13. Went 0.75 → 0.5 on 2026-07-31, when climbing felt too quick; back to 1 after BaseSpeed dropped to 2 — with the horizontal speed that low, half of it made gaining altitude a crawl.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[1] { AdminOnly(80) }));
			FlightTurnSpeed = config.Bind<float>("5 - Flight", "TurnSpeed", 200f, new ConfigDescription("Turn speed in the air, in degrees per second. Goes straight into m_flyTurnSpeed. The vanilla value is 12, which is a flying-creature number: at 12 a 180-degree turn takes fifteen seconds, and the 2026-07-31 playtest reported exactly that. 200 turns around in about a second. Lower feels heavier and makes high speed harder to steer.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 720f), new object[1] { AdminOnly(75) }));
			FlightAimDivePitch = config.Bind<float>("5 - Flight", "AimDivePitch", 150f, new ConfigDescription("Body tilt, in degrees, when diving straight down in aim flight mode. 0 is upright, 90 is lying flat belly down, 180 is head straight down. Shallower dives blend between the level-flight tilt and this value by the dive angle. Climbing and classic mode are not affected.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 180f), new object[1] { ClientSide(74) }));
			FlightSpeedSkillBonus = config.Bind<float>("5 - Flight", "SpeedSkillBonus", 0.5f, new ConfigDescription("Speed bonus at level 100 of the flight skill. 0.5 = +50%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), new object[1] { AdminOnly(70) }));
			FlightKiSkillReduction = config.Bind<float>("5 - Flight", "KiSkillReduction", 0.95f, new ConfigDescription("Fraction of the ki cost removed at level 100 of the flight skill. 0.95 = at max level flying costs a twentieth. Together with SpeedSkillBonus this is the whole progression of the skill: farther per point of ki. KiSkillCurve decides how much of it arrives before level 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.99f), new object[1] { AdminOnly(65) }));
			FlightKiSkillCurve = config.Bind<float>("5 - Flight", "KiSkillCurve", 1f, new ConfigDescription("Shape of the flight skill discount: reduction = KiSkillReduction * (level/100)^this. 1 is a straight line, so half the skill gives half the discount. Above 1 the saving is back-loaded — the last levels are where flight actually gets cheap, which is what keeps early flight expensive while still making level 100 feel like an arrival. Below 1 front-loads it. Check it with saiya_fly skill <level>.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 5f), new object[1] { AdminOnly(64) }));
			FlightKiPowerReduction = config.Bind<float>("5 - Flight", "KiPowerReduction", 0.0033f, new ConfigDescription("How much the late-game power term (Battle Power.K5_LateGameBonus) cheapens flight, hyperbolically: cost is multiplied by 1 / (1 + this * bonus). This is the ONLY thing the late-game term changes about flying — speed is deliberately left out of it, because speed already runs into MaxSpeed, which is a zone-streaming limit rather than balance. What being strong buys in the air is range, not velocity. Reads the late-game term alone and not total power, so early flight stays as expensive as KiPerSecond says — the reward belongs to the end of the game. 0 disables it. Check it with saiya_fly.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.1f), new object[1] { AdminOnly(63) }));
			FlightFormKiShare = config.Bind<float>("5 - Flight", "FormKiShare", 0.3f, new ConfigDescription("How much a transformation makes flying cost, as a fraction of the power it adds: cost is multiplied by 1 + (PowerMultiplier - 1) * this * (1 - mastery). At 0.3, SSJ (x2 power) makes flight 30% more expensive and SSJ3 (x4) 90%, and both fade to nothing at level 100 of THAT form's mastery. It mirrors FormSpeedShare deliberately: the form pays for the speed it gives. Note the payer is the form's mastery, which trains by FIGHTING transformed — flying transformed does not make flying transformed any cheaper. 0 gives the form's flight speed away for free, which is what happened before 2026-09-20. (Starting value. Not playtested yet.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[1] { AdminOnly(62) }));
			FlightXpPerMeter = config.Bind<float>("5 - Flight", "XpPerMeter", 0.075f, new ConfigDescription("Flight skill XP per METER flown. Flying is its own training — there is no other way to raise it — but hovering in place pays nothing, because nothing moves. Distance here is the path travelled, not the distance from where you took off: flying out and back pays for both legs, since both cost ki. Valheim's own diminishing curve up to 100 applies. (Replaced XpPerSecond on 2026-09-20 — 0.15/m is what 0.5/s was worth at base speed. The old key stays behind in existing config files, inert. Halved to 0.075 on 2026-09-20: the first playtest of the rework levelled far too fast.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { AdminOnly(60) }));
			FlightFallDamageSkillReduction = config.Bind<float>("5 - Flight", "FallDamageSkillReduction", 0.8f, new ConfigDescription("Fraction of fall damage removed at level 100 of the flight skill, linear in the level (0.8 = 40% less at level 50, 80% less at 100). Only while ki is on — an empty bar still protects, turning ki off does not. Flying itself never takes fall damage; this is for jumping off cliffs and dropping out of flight. Stacks multiplicatively with the game's own fall protection (feather cape). 0 disables it, 1 makes level 100 immune.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(58) }));
			FlightWeightPenalty = config.Bind<float>("5 - Flight", "WeightPenalty", 0.6f, new ConfigDescription("Fraction of the speed lost with the inventory at maximum weight. 0.5 = flying fully loaded you go at half speed. (Playtest value, 2026-08-11. Raised from 0.5: hauling a full inventory by air should cost something the player can feel.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.95f), new object[1] { AdminOnly(55) }));
			FlightWeightCurve = config.Bind<float>("5 - Flight", "WeightCurve", 5f, new ConfigDescription("Shape of the weight penalty: speed loss = WeightPenalty * (carried/max)^this. 1 is a straight line, so half loaded costs half the penalty. Above 1 the penalty is back-loaded — light and medium loads barely register and the speed falls off sharply as the inventory fills, which is what keeps casual looting from feeling taxed. Below 1 front-loads it. (Playtest value, 2026-09-06. Went straight to the top of the range: at 3 the penalty was still noticeable at half load, and the whole point of the curve was that only a full inventory should slow you down.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 5f), new object[1] { AdminOnly(54) }));
			FlightMaxSpeed = config.Bind<float>("5 - Flight", "MaxSpeed", 30f, new ConfigDescription("Hard speed cap, applied after every multiplier. An engine limit, not balance: above a certain speed zone streaming cannot keep up and the world loads in chunks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), new object[1] { AdminOnly(45) }));
			FlightMovementDebuffStrength = config.Bind<float>("5 - Flight", "MovementDebuffStrength", 1f, new ConfigDescription("How much movement-slowing status effects (bear traps, frost, tar) slow flight. 1 applies them in full, as on foot: a bear trap holds you in place in the air too. 0.5 applies half, so a trap slows flight by half instead of stopping it. 0 ignores them and flight escapes a trap. Turning is always slowed, as in vanilla. Speed buffs are never applied to flight.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { AdminOnly(44) }));
			PowerK1Health = config.Bind<float>("6 - Battle Power", "K1_Health", 1f, new ConfigDescription("Weight of HP in the battle power. Applies to both formulas. Only HP ABOVE the game's minimum (25) counts: those 25 are handed to any new character for free, and counting them would give everyone a floor of power nobody earned. (Fixed in the 2026-07-30 playtest.)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { AdminOnly(100) }));
			PowerK2WeaponDamage = config.Bind<float>("6 - Battle Power", "K2_WeaponDamage", 2f, new ConfigDescription("Weight of the equipped weapon's damage. Only used in the ki-OFF formula.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { AdminOnly(90) }));
			PowerK3Armor = config.Bind<float>("6 - Battle Power", "K3_Armor", 1.5f, new ConfigDescription("Weight of equipment armor. Only used in the ki-OFF formula — with ki on, armor is an output of the battle power, not an input.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { AdminOnly(80) }));
			PowerK4PowerSkill = config.Bind<float>("6 - Battle Power", "K4_PowerSkill", 3f, new ConfigDescription("Weight of the Power Level skill (0-100). Only used in the ki-ON formula. It is the only axis of progression for a ki user — without it, power would be constant from the