things/enemies/Boss.Invincible.cs

Boss partial class code that implements an invincible phase. It decides when to enter that phase based on HP percent, spawns generator enemies around the arena via RPC, plays sounds/animations, prevents death to trigger the phase, and tracks when generators are destroyed to end invincibility.

Networking
using System;
using Sandbox;
using Sandbox.Citizen;

public partial class Boss
{
	private bool _hasSpawnedGenerators;
	private int _numGenerators;

	private float _invincibleHpPercent;

	void InitInvincible()
	{
		if ( IsProxy )
			return;

		_invincibleHpPercent = Game.Random.Float( 0.25f, 0.33f );
	}

	void HandleInvincible()
	{
		if ( _hasSpawnedGenerators || IsSpawning || IsDying || IsInTheAir || IsProxy )
			return;

		if ( State == BossState.InvinciblePrepare || State == BossState.Invincible )
			return;

		if ( HpPercent < _invincibleHpPercent )
		{
			_hasSpawnedGenerators = true;
			EnterDefaultStateRpc();
			SetState( BossState.InvinciblePrepare );
		}
	}

	protected override void StartDying( Vector2 dir, float force, Player player, DamageType damageType )
	{
		// If the invincible phase hasn't triggered yet, prevent death and enter it now instead.
		if ( !_hasSpawnedGenerators && !IsSpawning )
		{
			Health = 1f;
			_hasSpawnedGenerators = true;
			EnterDefaultStateRpc();
			SetState( BossState.InvinciblePrepare );
			return;
		}

		base.StartDying( dir, force, player, damageType );
	}

	[Rpc.Broadcast( NetFlags.Reliable )]
	protected void StartInvincibleRpc()
	{
		SetAnim( "SummonPrepare" );

		Manager.Instance.PlaySfxNearby( "boss_invincible_start", Position2D, pitch: Game.Random.Float( 0.8f, 0.85f ), volume: 2.3f, maxDist: 2000f );

		//AnimationHelper.HoldType = CitizenAnimationHelper.HoldTypes.Punch;

		if ( IsProxy )
			return;

		_numGenerators = Utils.Select( Manager.Instance.Difficulty, 1, 2, 3 );

		if( _numGenerators <= 0 )
			return;

		var min = Manager.Instance.BOUNDS_MIN;
		var max = Manager.Instance.BOUNDS_MAX;
		var center = (min + max) * 0.5f;
		var radius = (max.x - min.x) * 0.5f - 200f;

		var startAngle = Game.Random.Float( 0f, 360f );
		var angleStep = 360f / _numGenerators;

		for ( int i = 0; i < _numGenerators; i++ )
		{
			var angle = startAngle + i * angleStep;
			var radians = angle * MathF.PI / 180f;
			var pos = center + new Vector2( MathF.Cos( radians ), MathF.Sin( radians ) ) * radius;
			Manager.Instance.SpawnEnemyRpc( EnemyType.BossInvincibleGenerator, pos );
		}

		BecomeInvincible();
	}

	[Rpc.Broadcast]
	protected void FinishInvincibleRpc()
	{
		SetAnim( "Summon" );
	}

	public void InvincibleGeneratorDestroyed()
	{
		_numGenerators--;

		if( _numGenerators <= 0 )
		{
			var invincible = GetUnitStatus<UnitStatusInvincible>();
			if( invincible != null )
			{
				RemoveUnitStatus( invincible );
			}
		}
	}
}