things/enemies/MinibossShielder.cs

Enemy subclass for a miniboss called MinibossShielder. It sets stats, spawn scale, healthbar behavior, spawns orbiting shield enemies during start and as health thresholds are crossed, and destroys shields on death.

NetworkingFile Access
using System;
using Sandbox;

public class MinibossShielder : Enemy
{
	public override EnemyType EnemyType => EnemyType.MinibossShielder;
	public override string GibFolder => "miniboss_shielder";
	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.6f );
	public override bool ShowHealthbar => true;
	public override float HealthbarOffset => 100f;
	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;

	private List<OrbiterShieldEnemy> _shields;
	private bool _rotateClockwise;

	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 = 9000f;
		Weight = 1.5f;

		_personalSpeedScale = 1f;
		_personalSpeedFreq = 12f;

		AnimSpeedModifier = 0.7f;

		if ( IsProxy )
			return;

		AggroRange = 85f;
		DetectTargetRange = 850f;
		LoseTargetRange = 1300f;
		LoseTargetTime = 7f;
		MeleeDamage = Utils.Select( Manager.Instance.Difficulty, 11f, 15f, 16f );
		DamageTargetDelay = 0.8f;
		Acceleration = 265f * Utils.Select( Manager.Instance.Difficulty, 0.9f, 1f, 1.05f );
		AccelerationAttacking = 305f * Utils.Select( Manager.Instance.Difficulty, 0.9f, 1f, 1.05f );
		Deceleration = 2.2f;
		DecelerationAttacking = 2.1f;

		_personalTurnSpeed = 4.5f;

		_rotateClockwise = Game.Random.Float( 0f, 1f ) < 0.5f;
		_shields = new();
		SpawnShield( 90f );
	}

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

		if ( IsProxy )
			return;

		if ( _shields.Count == 1 && HpPercent < 0.66f )
			SpawnShield( _shields[0].CurrOrbitAngleDegrees - 90f );
		else if ( _shields.Count == 2 && HpPercent < 0.33f )
			SpawnShield( _shields[1].CurrOrbitAngleDegrees - 90f );
	}

	protected override float GetMoveSpeedFactor()
	{
		var progress = ModelRenderer.SceneModel.CurrentSequence.TimeNormalized;
		return Charger.GetChargerMoveSpeedFactor( progress, IsAttacking );
	}

	[Rpc.Broadcast]
	void SpawnShield( float degrees )
	{
		// todo: sfx

		if ( IsProxy )
			return;

		var pos = Position2D;
		var orbiterShieldEnemyGo = GameObject.Clone( "prefabs/orbiter_shield_enemy.prefab", new CloneConfig { StartEnabled = true, Transform = new Transform( new Vector3( pos.x, pos.y, -999f ), Rotation.From( 0f, 0f, 0f ) ) } );
		var orbiterShieldEnemy = orbiterShieldEnemyGo.Components.Get<OrbiterShieldEnemy>( true );

		orbiterShieldEnemy.OrbitedUnit = this;
		orbiterShieldEnemy.CurrOrbitAngleDegrees = degrees;
		orbiterShieldEnemy.ShieldNum = _shields.Count;

		orbiterShieldEnemyGo.NetworkSpawn();

		_shields.Add( orbiterShieldEnemy );

		foreach ( var shield in _shields )
			shield.OrbitSpeed = (_shields.Count * 40f) * (_rotateClockwise ? -1f : 1f);
	}

	public override void Die( Vector2 dir, float force, Player player, DamageType damageType )
	{
		base.Die( dir, force, player, damageType );

		if ( IsProxy )
			return;

		foreach ( var shield in _shields )
			shield.GameObject.Destroy();
	}

}