Part of a Boss enemy class, implementing charging behavior. It initializes charge parameters, triggers charge preparation and execution via RPCs, computes target direction and velocity, handles timing and redirects, and reflects direction when going out of bounds.
using System;
using Sandbox;
using Sandbox.Citizen;
public partial class Boss
{
protected float _chargeDelayTimer;
protected float _chargeDelayMin;
protected float _chargeDelayMax;
protected float _chargeTime;
protected float _chargeTimeMin;
protected float _chargeTimeMax;
protected Vector2 _chargeDir;
protected Vector2 _chargeVel;
protected TimeSince _timeSinceChargeCloud;
protected float _chargeRange;
protected float _chargeRotateSpeed;
private float _nextRedirectTime;
private TimeSince _timeSinceRedirect;
protected float _chargeWeight;
void InitCharging()
{
if ( IsProxy )
return;
_chargeRange = 900f;
_chargeTimeMin = 2.5f;
_chargeTimeMax = 6.25f;
_chargeDelayTimer = Game.Random.Float( 8f, 12f ) * Utils.Select( Manager.Instance.Difficulty, 1.3f, 1f, 0.9f );
_chargeDelayMin = 2f;
_chargeDelayMax = 10f;
_chargeRotateSpeed = 5f;
_chargeWeight = 15f;
}
void HandleCharging()
{
if ( !IsReadyForAction || !TargetUnit.IsValid() )
return;
var targetDistSqr = (TargetUnit.Position2D - Position2D).LengthSquared;
if ( targetDistSqr < MathF.Pow( _chargeRange, 2f ) )
{
_chargeDelayTimer -= Time.Delta * TimeScale;
if ( _chargeDelayTimer < 0f )
SetState( BossState.ChargePrepare );
}
}
protected override void HandleMovement()
{
if ( State == BossState.Charge )
return;
base.HandleMovement();
}
[Rpc.Broadcast]
public void PrepareToChargeRpc()
{
Manager.Instance.PlaySfxNearby( "boss.prepare", Position2D, pitch: Game.Random.Float( 1.05f, 1.1f ), volume: 1.4f, maxDist: 500f );
SetAnim( "ChargePrepare" );
//AnimationHelper.DuckLevel = 1f;
CanAnimate = false;
}
[Rpc.Broadcast]
public void ChargeRpc()
{
Manager.Instance.PlaySfxNearby( "boss.charge", Position2D, pitch: Game.Random.Float( 0.9f, 1.05f ), volume: 1.4f, maxDist: 750f );
SetAnim( "Charge" );
//AnimationHelper.SpecialMove = CitizenAnimationHelper.SpecialMoveStyle.Roll;
//SetPlaybackRate( 3f );
//AnimationHelper.DuckLevel = 0f;
_timeSinceChargeCloud = 0f;
if ( IsProxy )
return;
Player closestPlayer = Manager.Instance.GetClosestPlayer( Position2D );
if ( !closestPlayer.IsValid() )
return;
var targetPos = closestPlayer.Position2D + closestPlayer.Velocity * Game.Random.Float( 0.2f, 2f ) + new Vector2( Game.Random.Float( -1f, 1f ), Game.Random.Float( -1f, 1f ) ) * 100f;
Vector2 targetDir = (targetPos - Position2D).LengthSquared > Manager.TOUCH_DIST_REQUIRED_SQR
? (targetPos - Position2D).Normal
: Utils.GetRandomVector();
_chargeDir = Utils.RotateVector( targetDir, Game.Random.Float( -10f, 10f ) );
_chargeTime = Game.Random.Float( _chargeTimeMin, _chargeTimeMax );
_chargeVel = Vector2.Zero;
_nextRedirectTime = Game.Random.Float( 0.25f, 1f );
Weight = _chargeWeight;
}
protected override void OnOutOfBounds( Direction direction )
{
base.OnOutOfBounds( direction );
if ( direction == Direction.Left )
{
_chargeVel = new Vector2( Math.Abs( _chargeVel.x ), _chargeVel.y );
_chargeDir = new Vector2( Math.Abs( _chargeDir.x ), _chargeDir.y );
}
else if ( direction == Direction.Right )
{
_chargeVel = new Vector2( -Math.Abs( _chargeVel.x ), _chargeVel.y );
_chargeDir = new Vector2( -Math.Abs( _chargeDir.x ), _chargeDir.y );
}
else if ( direction == Direction.Down )
{
_chargeVel = new Vector2( _chargeVel.x, Math.Abs( _chargeVel.y ) );
_chargeDir = new Vector2( _chargeDir.x, Math.Abs( _chargeDir.y ) );
}
else if ( direction == Direction.Up )
{
_chargeVel = new Vector2( _chargeVel.x, -Math.Abs( _chargeVel.y ) );
_chargeDir = new Vector2( _chargeDir.x, -Math.Abs( _chargeDir.y ) );
}
}
}