things/enemies/Boss.Sword.cs

Partial Boss enemy component handling its sword-summoning attack. Initializes timing parameters, checks distance to trigger a prepare state, broadcasts RPCs to play animations and SFX, and spawns "sword" enemies at computed target positions over a timed sequence.

Networking
using Sandbox;
using Sandbox.Citizen;
using System;
using System.Xml.Linq;

public partial class Boss
{
	protected float _swordDelayTimer;
	protected float _swordDelayMin;
	protected float _swordDelayMax;

	private TimeSince _timeSinceSwordShoot;

	private float _swordEmitDelay;
	private float _swordEmitDelayMin;
	private float _swordEmitDelayMax;

	private int _swordEmitNum;

	private float _swordTotalTime;

	void InitSword()
	{
		if ( IsProxy )
			return;

		_swordDelayMin = 15f;
		_swordDelayMax = 34f;

		_swordDelayTimer = Game.Random.Float( _swordDelayMin, _swordDelayMax ) * Utils.Select( Manager.Instance.Difficulty, 1.35f, 1f, 0.925f );
		//_swordDelayTimer = 0.6f;

		_swordEmitDelayMin = 1.2f;
		_swordEmitDelayMax = 1.6f;
	}

	void HandleSword()
	{
		if ( !IsReadyForAction || !TargetUnit.IsValid() )
			return;

		var targetDistSqr = (TargetUnit.Position2D - Position2D).LengthSquared;

		if ( targetDistSqr < MathF.Pow( 800f, 2f ) )
		{
			_swordDelayTimer -= Time.Delta * TimeScale;
			if ( _swordDelayTimer < 0f )
				SetState( BossState.SwordPrepare );
		}
	}

	[Rpc.Broadcast]
	protected void StartSwordRpc()
	{
		SetAnim( "SummonPrepare" );

		//AnimationHelper.HoldType = CitizenAnimationHelper.HoldTypes.RPG;
		//AnimationHelper.WithLook( Vector3.Up );

		Manager.Instance.PlaySfxNearby( "boss.prepare", Position2D, pitch: Game.Random.Float( 0.75f, 0.85f ), volume: 1.4f, maxDist: 500f );

		if ( IsProxy )
			return;

		_swordEmitNum = 0;

		_swordEmitDelay = Game.Random.Float( _swordEmitDelayMin, _swordEmitDelayMax ) * Utils.Map( HpPercent, 1f, 0f, 1f, 0.75f ); 
		_swordTotalTime = Game.Random.Float( 7f, 9f ) * Utils.Map( HpPercent, 1f, 0f, 1f, 1.33f, EasingType.Linear );

		_timeSinceSwordShoot = _swordEmitDelay - Game.Random.Float( 0.1f, 0.2f );
	}

	[Rpc.Broadcast]
	protected void SwordRpc()
	{
		//AnimationHelper.Target.Set( "b_attack", true );

		if ( IsProxy )
			return;
	}

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

		// todo: better sfx
		//Manager.Instance.PlaySfxNearby( "boss.shoot", Position2D, pitch: Utils.Map( _swordEmitNum, 0, 10, 0.3f, 0.4f, EasingType.SineOut), volume: 0.8f, maxDist: 600f );

		//AnimationHelper.Target.Set( "b_attack", true );

		if ( IsProxy )
			return;

		var targetPos = (HasTarget && !TargetUnit.IsDying)
			? TargetUnit.Position2D + TargetUnit.Velocity * Game.Random.Float( 0.5f, 3f ) + TargetUnit.Velocity.Normal * Game.Random.Float( 0f, 60f ) + Utils.GetRandomVector() * Game.Random.Float( 30f, 90f )
			: Position2D + Utils.GetRandomVector() * Game.Random.Float( 100f, 400f );

		Manager.Instance.SpawnEnemy( "sword", targetPos, spawnInstantly: false, rotAngle: 90f + Game.Random.Float( -40f, 40f ) + (Game.Random.Int( 0, 1 ) == 0 ? 180f : 0f) );

		_swordEmitNum++;
	}
}