Units/EnemyChallengeReceiver.cs
using System;

namespace PlanetMeat;

[Group( "Planet Meat - Units" ), Title( "Enemy Challenge Receiver" ), Icon( "addchart" )]
public sealed class EnemyChallengeReceiver : Component
{
	const float HP_SCALE = 0.01f;

	[Property, ReadOnly] public int MaxHp { get; private set; } = -1;
	[Property, ReadOnly] public float MoveSpeed { get; private set; }
	[Property, ReadOnly] public float SpeedMult { get; private set; }
	[Property, ReadOnly] public float DamageMult { get; private set; }
	public float MinAttackSpeed { get; private set; }
	public float MaxAttackSpeed { get; private set; }
	public int MinDamage { get; private set; }
	public int MaxDamage { get; private set; }

	private int GetDamage( double baseValue ) => Progressions.Intify( baseValue * DamageMult );

	public void Bake()
	{
		var round = Agenda.Current.RoundNum;
		var wave = Agenda.Current.WaveNum;
		var slowRamp = Progressions.ExpSlow( 3 * (round - 1) + wave ) - 1;

		SpeedMult = Difficulty.Current.GetMult( "EnemySpeed" );
		if ( Components.Get<NavMeshAgent>() is NavMeshAgent navAgent )
			MoveSpeed = (navAgent.MaxSpeed + (5.0f * round + 0.5f * wave)) * SpeedMult;

		DamageMult = Difficulty.Current.GetMult( "EnemyDmg" );
		var firstDmg = true;
		foreach ( var w in Components.GetAll<IEnemyAttackReceiver>() )
		{
			var dmg = GetDamage( w.BaseDamage + slowRamp );
			if ( firstDmg )
			{
				firstDmg = false;
				MinDamage = MaxDamage = dmg;
			}
			else
			{
				MinDamage = Math.Min( MinDamage, dmg );
				MaxDamage = Math.Max( MaxDamage, dmg );
			}
		}

		var firstWep = true;
		foreach ( var w in Components.GetAll<AutoWeapon>() )
		{
			var recharge = w.BaseRecharge * SpeedMult;
			if ( firstWep )
			{
				firstWep = false;
				MinAttackSpeed = MaxAttackSpeed = recharge;
			}
			else
			{
				MinAttackSpeed = Math.Min( MinAttackSpeed, recharge );
				MaxAttackSpeed = Math.Max( MaxAttackSpeed, recharge );
			}
		}

		if ( Components.Get<Targetable>() is Targetable targ )
			MaxHp = Progressions.Intify( slowRamp * targ.BaseMaxHp * HP_SCALE * Difficulty.Current.GetMult( "EnemyHp" ) );
	}

	protected override void OnStart()
	{
		base.OnStart();
		if ( MaxHp < 0 )
			Bake();

		var round = Agenda.Current.RoundNum;
		var roundCrawl = Progressions.StairCrawlInt( round ) - 1;

		if ( Components.Get<NavMeshAgent>() is NavMeshAgent navAgent )
		{
			var accelFraction = navAgent.Acceleration / navAgent.MaxSpeed;
			navAgent.MaxSpeed = MoveSpeed;
			navAgent.Acceleration = accelFraction * MoveSpeed;
		}

		foreach ( var w in Components.GetAll<IEnemyAttackReceiver>() )
			w.BaseDamage = GetDamage( w.BaseDamage + roundCrawl );
		foreach ( var w in Components.GetAll<AutoWeapon>() )
			w.BaseRecharge *= SpeedMult;

		if ( Components.Get<Targetable>() is Targetable targ )
			targ.BaseMaxHp = MaxHp;
	}
}