AI helper for a bot car that decides when to trigger boost. It examines steering, fuel fraction from CarBoost, nearby obstacles via ObstacleScanResult, aggression, and sprint availability, then sets CarInputData.Boost and sometimes adjusts steering for side rams.
namespace Machines.Player;
/// <summary>
/// Decides when to boost for a bot
/// </summary>
public sealed class BotBoost
{
/// <summary>
/// Minimum fuel fraction to attempt a sprint boost
/// </summary>
private const float SprintFuelThreshold = 0.3f;
/// <summary>
/// Minimum fuel fraction to attempt a ram boost
/// </summary>
private const float RamFuelThreshold = 0.2f;
/// <summary>
/// Maximum steer magnitude for a sprint boost (must be going roughly straight)
/// </summary>
private const float SprintMaxSteer = 0.4f;
/// <summary>
/// Maximum distance (units) for a car to be considered rammable
/// </summary>
private const float RamDistance = 130f;
/// <summary>
/// Cooldown between boost requests; must exceed CarBoost.DashCooldown
/// </summary>
private const float BoostInterval = 0.8f;
private float _nextBoostTime;
private bool _boostedLastFrame;
/// <summary>
/// Decides whether to boost; writes <see cref="CarInputData.Boost"/> and may adjust <see cref="CarInputData.Steer"/> for ram targeting
/// </summary>
public void Apply( ref CarInputData data, CarBoost boost, ObstacleScanResult scan, float aggression, bool canSprint )
{
if ( data.Drift )
return;
if ( !boost.IsValid() || boost.IsLockedOut )
return;
// Must release for at least one frame between boosts (rising-edge requirement)
if ( _boostedLastFrame )
{
_boostedLastFrame = false;
return;
}
// Respect cooldown
if ( Time.Now < _nextBoostTime )
return;
// Sprint boost: clear straight, throttling, enough fuel
if ( canSprint && MathF.Abs( data.Steer ) < SprintMaxSteer && boost.BoostFraction > SprintFuelThreshold )
{
FireBoost( ref data );
return;
}
// Ram boost: car nearby, aggression check passes
if ( scan.HasFrontCar && scan.FrontDistance < RamDistance && boost.BoostFraction > RamFuelThreshold )
{
if ( Game.Random.Float() < aggression * 0.02f )
{
FireBoost( ref data );
return;
}
}
// Side ram: if a car is to our side (detected via clearance difference), steer into it + boost
if ( aggression > 0.5f && boost.BoostFraction > RamFuelThreshold )
{
var sideDiff = scan.RightClearance - scan.LeftClearance;
var closeSide = MathF.Min( scan.LeftClearance, scan.RightClearance );
if ( closeSide < 80f && MathF.Abs( sideDiff ) > 60f )
{
if ( Game.Random.Float() < (aggression - 0.5f) * 0.015f )
{
data.Steer = (data.Steer + (sideDiff > 0f ? -0.4f : 0.4f)).Clamp( -1f, 1f );
FireBoost( ref data );
return;
}
}
}
}
private void FireBoost( ref CarInputData data )
{
data.Boost = true;
_boostedLastFrame = true;
_nextBoostTime = Time.Now + BoostInterval;
}
}
/// <summary>
/// Lightweight scan result for <see cref="BotBoost"/>, decoupled from the brain's internal struct
/// </summary>
public struct ObstacleScanResult
{
public bool HasFrontCar;
public float FrontDistance;
public float LeftClearance;
public float RightClearance;
}