Boss enemy shooting behavior. Initializes shoot timers and range, decides when to enter shooting state, and RPCs to play prepare and perform shooting which spawn homing or regular projectiles and play SFX.
using System;
using Sandbox;
using Sandbox.Citizen;
public partial class Boss
{
protected float _shootDelayTimer;
protected float _shootDelayMin;
protected float _shootDelayMax;
protected float _shootRange;
void InitShooting()
{
if ( IsProxy )
return;
_shootDelayMin = 3.5f;
_shootDelayMax = 8.5f;
_shootRange = 500f;
_shootDelayTimer = Game.Random.Float( _shootDelayMin, _shootDelayMax ) * Utils.Select( Manager.Instance.Difficulty, 1.3f, 1f, 0.925f );
}
void HandleShooting()
{
if ( !IsReadyForAction || !TargetUnit.IsValid() )
return;
var targetDistSqr = (TargetUnit.Position2D - Position2D).LengthSquared;
if ( targetDistSqr < MathF.Pow( _shootRange, 2f ) )
{
_shootDelayTimer -= Time.Delta * TimeScale;
if ( _shootDelayTimer < 0f && targetDistSqr < MathF.Pow( _shootRange * 0.85f, 2f ) )
SetState( BossState.ShootPrepare );
}
}
[Rpc.Broadcast]
protected void StartShootingRpc()
{
SetAnim( "ShootPrepare" );
//AnimationHelper.HoldType = CitizenAnimationHelper.HoldTypes.Pistol;
Manager.Instance.PlaySfxNearby( "boss.prepare", Position2D, pitch: Game.Random.Float( 0.75f, 0.85f ), volume: 1.4f, maxDist: 500f );
}
[Rpc.Broadcast]
protected void ShootRpc()
{
SetAnim( "Shoot" );
//AnimationHelper.Target.Set( "b_attack", true );
if ( IsProxy )
return;
var dir = (Vector2)WorldRotation.Forward;
var homingChance = Utils.Map( HpPercent, 1f, 0f, 0f, 0.6f, EasingType.Linear );
if ( Game.Random.Float( 0f, 1f ) < homingChance )
{
var numBullets = MathX.FloorToInt( Utils.Map( HpPercent, 0.85f, 0f, 1f, 6f ) );
var spread = 360f;
float currAngleOffset = 0f;
float increment = numBullets > 1 ? (spread / (float)numBullets) : 0f;
for ( int i = 0; i < numBullets; i++ )
{
var currDir = Utils.RotateVector( dir, currAngleOffset + increment * i );
var pos = Position2D + currDir * 20f;
// todo: different types on harder difficulties
EnemyProjectileType projectileType = EnemyProjectileType.Normal;
Manager.Instance.SpawnEnemyHomingProjectile( pos, currDir, shooter: this, enemyType: this.EnemyType, startVel: Game.Random.Float( 50f, 90f ) * Utils.Select( Manager.Instance.Difficulty, 0.9f, 1f, 1.25f ), projectileType );
}
//Manager.Instance.PlaySfxNearbyRpc( "boss.shoot", Position2D, pitch: Game.Random.Float( 0.35f, 0.4f ), volume: 1.3f, maxDist: 600f );
Manager.Instance.PlaySfxNearbyRpc( "squelch", Position2D, pitch: Game.Random.Float( 1f, 1.1f ), volume: 2.4f, maxDist: 600f );
}
else
{
var numBullets = MathX.FloorToInt( Utils.Map( HpPercent, 1f, 0f, 3f, 8f, EasingType.SineIn ) ) + Game.Random.Int( 0, 1 );
var spread = Game.Random.Float( 30f, 80f );
float currAngleOffset = -spread * 0.5f;
float increment = numBullets > 1 ? (spread / (float)(numBullets - 1)) : 0f;
for ( int i = 0; i < numBullets; i++ )
{
var currDir = Utils.RotateVector( dir, currAngleOffset + increment * i );
var pos = Position2D + currDir * 20f;
var vel = Utils.Select( Manager.Instance.Difficulty, 200f, 220f, 260f );
// todo: different types on harder difficulties
EnemyProjectileType projectileType = EnemyProjectileType.Normal;
Manager.Instance.SpawnEnemyProjectile( pos, currDir, shooter: this, enemyType: this.EnemyType, startVel: vel, projectileType );
}
Manager.Instance.PlaySfxNearbyRpc( "squelch", Position2D, pitch: Game.Random.Float( 1.2f, 1.3f ), volume: 2.4f, maxDist: 600f );
}
}
}