things/enemies/MinibossCharger.cs

Enemy class MinibossCharger, a subclass of Charger. It sets miniboss-specific properties like health scale, visuals, coin values, movement and charge parameters, and overrides Colliding to fling nearby enemies or players when charging.

Networking
using System;
using Sandbox;

public class MinibossCharger : Charger
{
	public override EnemyType EnemyType => EnemyType.MinibossCharger;
	public override string GibFolder => "miniboss_charger";
	public override float OverrideGibChance => 1f;
	public override int ExtraDeathBloodSprayAmount => 25;
	protected override float MinibossHealthScale => 1.2f;
	public override float GetMaxHealth() => MinibossBaseHealth * MinibossHealthScale;

	public override Vector3 SpawnScale => new Vector3( 1.5f );
	public override bool ShowHealthbar => true;
	public override float HealthbarOffset => State == ChargerState.Charge ? 70f : 105f;
	public override float HealthbarOpacity => Utils.EasePercent( SpawnProgress, EasingType.QuadIn );
	public override float HealthbarArmorOpacity => Utils.EasePercent( SpawnProgress, EasingType.QuadIn );

	public override bool IsBoss => true;
	public override bool IsMiniboss => true;

	public override float ParticleYPosOverride => 0.5f;
	public override float StunParticleYPosOverride => 0.85f;

	protected override void OnStart()
	{
		base.OnStart();

		CoinValueMin = 10;
		CoinValueMax = 18;
		CoinChance = 1f;

		PushStrength = 8000f;
		Weight = _baseWeight = 2.3f;
		_chargeWeight = 4.5f;

		_personalSpeedScale = 1f;
		_personalSpeedFreq = Game.Random.Float( 9f, 11f );

		if ( IsProxy )
			return;

		AggroRange = 70f;
		DetectTargetRange = 900f;
		LoseTargetRange = 1400f;
		LoseTargetTime = 9f;
		MeleeDamage = Utils.Select( Manager.Instance.Difficulty, 9f, 13f, 14f );
		DamageTargetDelay = 0.8f;
		Acceleration = 300f;
		AccelerationAttacking = 350f;
		Deceleration = 1.5f;
		DecelerationAttacking = 1.4f;

		_personalTurnSpeed = Game.Random.Float( 5f, 7f );
		_personalChargeRange = 650f;

		_chargeTimeMin = 2.5f;
		_chargeTimeMax = 5f;
		_chargeDelayMin = 2.2f;
		_chargeDelayMax = 5f;
		_chargeDelayTimer = Game.Random.Float( _chargeDelayMin, _chargeDelayMax );
		_chargeRotateSpeed = 9f;
		_chargeVelMax = 500f;
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

		if ( IsProxy )
			return;

	}

	public override void Colliding( Thing other, float percent, float dt )
	{
		base.Colliding( other, percent, dt );

		if ( IsInTheAir )
			return;

		if ( State == ChargerState.Charge && _timeSinceChangeState < _chargeTime - 0.4f )
		{
			if ( other is Enemy enemy && !(enemy is Boss) )
			{
				if ( !enemy.IsInTheAir && !enemy.IsSpawning )
				{
					var targetPos = TargetUnit.IsValid() && Game.Random.Float( 0f, 1f ) < 0.5f
						? TargetUnit.Position2D + TargetUnit.Velocity * Game.Random.Float( 0f, 4f ) + Velocity * Game.Random.Float( 0f, 3f ) + Utils.GetRandomVector() * Game.Random.Float(80f, 350f)
						: Position2D + Velocity * Game.Random.Float( 0.5f, 4f ) + new Vector2( Game.Random.Float( -1f, 1f ), Game.Random.Float( -1f, 1f ) ) * 120f;

					enemy.JumpRpc( Manager.Instance.ClampPosToBounds( targetPos ), height: Game.Random.Float( 270f, 320f ), lifetime: Game.Random.Float( 1.1f, 1.5f ) );
					// todo: sfx
				}
			}
			else if ( other is Player player && !player.IsDead && !player.IsInTheAir && !player.IsInvincible )
			{
				var targetPos = TargetUnit.IsValid()
						? TargetUnit.Position2D + _chargeDir * Game.Random.Float( 200f, 400f ) + Velocity * Game.Random.Float( 0f, 3f ) + new Vector2( Game.Random.Float( -1f, 1f ), Game.Random.Float( -1f, 1f ) ) * 120f
						: Position2D + _chargeDir * Game.Random.Float( 200f, 400f ) + new Vector2( Game.Random.Float( -1f, 1f ), Game.Random.Float( -1f, 1f ) ) * 120f;

				player.JumpRpc( Manager.Instance.ClampPosToBounds( targetPos ), height: Game.Random.Float( 270f, 320f ), lifetime: Game.Random.Float( 1.1f, 1.5f ) );
				// todo: sfx
				// todo: if player dodges, don't fling them?
			}
		}
	}

}