Context object passed to maneuver implementations. Holds references to the controlled VehicleController, its Rigidbody, a TelemetryAccumulator, a per-run timer, parsed float parameters, and helpers to stage drive inputs and query heading.
namespace VehicleProto;
/// <summary>
/// Per-run/per-tick context handed to an <see cref="IManeuver"/>. Bundles the controlled car, its
/// rigidbody, the live elapsed run time, the parsed spec params, and the shared
/// <see cref="TelemetryAccumulator"/> (maneuvers read cumulative yaw / contact-loss / landing state
/// from it rather than re-deriving), plus the <see cref="Drive"/> input-staging helper and heading
/// math. A maneuver NEVER applies forces — it only stages <see cref="DriveInputs"/> through
/// <see cref="Drive"/>, driving the exact input path a player uses (the same input-staging
/// pattern), exactly as the monolithic pilot did.
/// </summary>
public sealed class ManeuverContext
{
public VehicleController Car { get; }
public Rigidbody Body { get; }
public TelemetryAccumulator Telemetry { get; }
/// <summary>Seconds since the maneuver started (the pilot's live run timer).</summary>
public float RunTime { get; internal set; }
IReadOnlyDictionary<string, float> _params;
public ManeuverContext( VehicleController car, Rigidbody body, TelemetryAccumulator telemetry )
{
Car = car;
Body = body;
Telemetry = telemetry;
}
public void SetParams( IReadOnlyDictionary<string, float> pars ) => _params = pars;
public float Param( string key, float fallback )
=> _params != null && _params.TryGetValue( key, out var v ) ? v : fallback;
/// <summary>Stage driver intent for this tick through the controller's input-override seam.</summary>
public void Drive( float forward, float steer, bool handbrake )
{
Car.InputOverride = new DriveInputs
{
MoveForward = Math.Clamp( forward, -1f, 1f ),
Steer = Math.Clamp( steer, -1f, 1f ),
Handbrake = handbrake,
};
}
public float HeadingDeg() => TelemetryAccumulator.HeadingDeg( Car );
}