things/enemies/Boss.Chain.cs

Boss enemy partial class handling chain attack behavior. Initializes chain timers and range, decides when to prepare/throw chains at target players, plays SFX and spawns chain hook prefabs with direction, distance and lifetime.

NetworkingFile Access
using System;
using Sandbox;
using Sandbox.Citizen;

public partial class Boss
{
	protected float _chainDelayTimer;
	protected float _chainDelayMin;
	protected float _chainDelayMax;

	protected float _chainRange;

	void InitChain()
	{
		if ( IsProxy )
			return;

		_chainDelayMin = 10f;
		_chainDelayMax = 17f;
		_chainRange = 700f;

		_chainDelayTimer = Game.Random.Float( _chainDelayMin, _chainDelayMax ) * Utils.Select( Manager.Instance.Difficulty, 3f, 1.2f, 1f );
		//_chainDelayTimer = 0.5f;
	}

	void HandleChain()
	{
		if ( Manager.Instance.Difficulty == 0 || !IsReadyForAction || !TargetUnit.IsValid() )
			return;

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

		if ( targetDistSqr < MathF.Pow( _chainRange, 2f ) )// && !TargetUnit.IsChained )
		{
			// todo: throw chain more often as the fight progresses
			_chainDelayTimer -= Time.Delta * TimeScale;

			if ( _chainDelayTimer < 0f && targetDistSqr < MathF.Pow( _chainRange * 0.85f, 2f ) )
				SetState( BossState.ChainPrepare );
		}
	}

	[Rpc.Broadcast]
	protected void StartChainRpc()
	{
		SetAnim( "ShootPrepare" );
		//AnimationHelper.HoldType = CitizenAnimationHelper.HoldTypes.Rifle;

		Manager.Instance.PlaySfxNearby( "boss_chain_prepare", Position2D, pitch: Game.Random.Float( 0.98f, 1.02f ), volume: 2f, maxDist: 1000f );
	}

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

		Manager.Instance.PlaySfxNearby( "boss_chain_throw", Position2D, pitch: Game.Random.Float( 1.1f, 1.15f ), volume: 2.2f, maxDist: 800f );

		if ( IsProxy )
			return;

		foreach( var player in Manager.Instance.AlivePlayers )
		{
			//if ( player.IsChained )
			//	continue;

			var distSqr = (player.Position2D - Position2D).LengthSquared;
			if ( distSqr < MathF.Pow( _chainRange, 2f ) )
			{
				var targetPos = player.Position2D + player.Velocity * Utils.Map( distSqr, 0f, 700f * 700f, 0.25f, 2f ) * Game.Random.Float( 0.8f, 1.2f );
				var dir = (targetPos - Position2D).Normal;
				ThrowChain( dir );
			}
		}
	}

	void ThrowChain( Vector2 dir )
	{
		var pos = Position2D + dir * 20f;
		var zPos = 50f;

		var chainGo = GameObject.Clone( "prefabs/chain_hook.prefab", new CloneConfig { StartEnabled = true, Transform = new Transform( new Vector3( pos.x, pos.y, zPos ), Rotation.From( 0f, -Utils.GetAngleDegreesFromVector( dir ), 0f ) ) } );
		var chain = chainGo.Components.Get<ChainHook>( true );

		chain.AnchorUnit = this;
		chain.Direction = dir;
		chain.Distance = Game.Random.Float( 390f, 410f );
		chain.Lifetime = Game.Random.Float( 1.65f, 1.75f );

		chainGo.NetworkSpawn();
	}
}