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