things/enemies/Boss.Chain.cs

Part of the Boss enemy behavior, implements the chain-throwing attack. Initializes timers and ranges, decides when to prepare and throw chains at nearby players, plays animations and sound, spawns chain hook prefabs and configures them.

NetworkingExternal Download
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();
	}
}