Vehicle
Present on ctx.vehicle while the entity is a running vehicle: a vehicle component on a dynamic body with a collider.
Null otherwise. Who drives. With the component's input: "auto" (the default) the engine drives it from the input actions throttle, brake, steer and handbrake every tick — keyboard, pad and POST /input alike — and a behaviour only reads it. With input: "script" the engine reads nothing and a behaviour drives it with {@link Vehicle.drive}; drive on an auto vehicle changes nothing and is counted on GET /perf → unwired.vehicle_ignored. Signs. Forward is the entity's -Z. speed is negative while reversing. Right is positive throughout: steer in drive is +1 right, as the action reads, and steerAngle is the wheels' angle in radians, positive right. ts // A cruise control: hold 12 m/s on the straight, whatever the driver's foot says. update(ctx) { const v = ctx.vehicle! v.drive({ throttle: v.speed < 12 ? 1 : 0, brake: v.speed > 13 ? 0.3 : 0 }) ctx.hud.set("speed", Math.round(v.speed * 3.6) + " km/h") }
Signature
export interface Vehicle {
/** Holds these pedals and this steering from the next tick until the next call (`input: "script"` only). */
drive(cmd: VehicleDrive): void
/** Puts the vehicle back on its wheels, upright and still, where every wheel last touched the ground. */
reset(): void
/** `"auto"`: the engine drives it from the input actions. `"script"`: a behaviour does. */
readonly input: "auto" | "script"
/** m/s along the vehicle's forward (-Z); negative while reversing. */
readonly speed: number
/** The front wheels' angle, radians, **positive right** — the sign the `steer` action and {@link Vehicle.drive} use. */
readonly steerAngle: number
/** The throttle and brake in force, 0..1 (a brake at a standstill has become reverse). */
readonly throttle: number
readonly brake: number
/** 1 forward, -1 reverse. */
readonly gear: number
readonly wheelsOnGround: number
/** The tightest circle it can turn at its speed now, metres (the lock left after `steering.speedReduction`): a bend of radius under this needs it slower. `GET /entities` reports the same `minTurnRadius`. */
readonly minTurnRadius: number
/** Seconds since any wheel last touched the ground; 0 while one is down. For a landing effect that scales with the flight, or to ignore a hop that is too short to count. */
readonly airTime: number
/** The last touchdown (a flight that ended with a wheel meeting the ground), as of the last tick. All 0 before the first landing. */
readonly landing: VehicleLanding
/** True while a drift is held: a vehicle with a `drift` block, its handbrake input down, steering above its minimum speed and angle. False always on a vehicle with no `drift` block. */
readonly drifting: boolean
/** The angle between where it points and where it travels while it drifts, radians, **positive right** like {@link Vehicle.steerAngle}; 0 when not drifting. */
readonly driftAngle: number
/** Seconds of boost-earning slide stored by the drift so far; 0 when not drifting. */
readonly driftCharge: number
/** The boost tier the charge has reached: 0 none, 1 the first of the vehicle's `boostTiers`, and so on. What a game turns into sparks and sounds. */
readonly driftTier: number
/** True while a boost is still pushing: a mini-boost earned by a drift, or one {@link Vehicle.boost} started. */
readonly boosting: boolean
/** The slip angle the `handling` guard saw on the last tick: between where it points and where it travels, radians, **positive right**. 0 always on a vehicle with no `handling` block. */
readonly slip: number
/** True while any wheel is on an `offRoad` surface (as of the last tick); false always on a vehicle with no `offRoad` block. */
readonly offRoad: boolean
/** True for `recovery.protectFor` seconds after an automatic recovery (`vehicle-recovered`); false always on a vehicle with no `recovery` block. */
readonly recovering: boolean
/** The last automatic recovery: why, and the world time it happened in seconds; null before any, and always on a vehicle with no `recovery` block. */
readonly lastRecovery: { why: VehicleRecoveryWhy; at: number } | null
/**
* Starts a boost: `speed` m/s on the top speed for `seconds`, with the `drift` block's `boostThrust` on the
* drive force (1.3 on a vehicle without a `drift` block), fading out over its last 30% as a drift's
* mini-boost does. Like that one it pushes while the throttle is held. Returns false, changing nothing,
* when a boost already running gives more speed than this one; a drift's own boost replaces it when cashed in.
* Both numbers must be finite and above 0, or it throws `vehicle.boost-invalid`. It boosts the wheels'
* drive: a watercraft's water thrust is not boosted yet, so on a boat it starts (and reads `boosting`)
* but does not push.
*
* ```ts
* // A boost pad: any kart driving over it gets 6 m/s more for 1.5 s.
* onTriggerEnter(ctx, other) {
* other.vehicle?.boost(6, 1.5)
* }
* ```
*/
boost(speed: number, seconds: number): boolean
/** Its wheels, in the vehicle's order: each one's {@link Wheel.drivenBy} is `"vehicle"`. On an `input: "script"` vehicle a write to one takes it over until the next {@link Vehicle.drive}; on an `auto` one it changes nothing. */
readonly wheels: Wheel[]
/** What its water handling did on the last tick (a vehicle with a `water` block; `null` on every other): the hooks a wake, a bow spray or a splash sound reads. */
readonly water: VehicleWater | null
}
Import: import type { Vehicle } from "@argus/api"
Members
| Member | Signature | Description |
|---|---|---|
drive | drive(cmd: VehicleDrive): void | Holds these pedals and this steering from the next tick until the next call (input: "script" only). |
reset | reset(): void | Puts the vehicle back on its wheels, upright and still, where every wheel last touched the ground. |
input | readonly input: "auto" | "script" | "auto": the engine drives it from the input actions. "script": a behaviour does. |
speed | readonly speed: number | m/s along the vehicle's forward (-Z); negative while reversing. |
steerAngle | readonly steerAngle: number | The front wheels' angle, radians, positive right — the sign the steer action and {@link Vehicle.drive} use. |
throttle | readonly throttle: number | The throttle and brake in force, 0..1 (a brake at a standstill has become reverse). |
brake | readonly brake: number | |
gear | readonly gear: number | 1 forward, -1 reverse. |
wheelsOnGround | readonly wheelsOnGround: number | |
minTurnRadius | readonly minTurnRadius: number | The tightest circle it can turn at its speed now, metres (the lock left after steering.speedReduction): a bend of radius under this needs it slower. GET /entities reports the same minTurnRadius. |
airTime | readonly airTime: number | Seconds since any wheel last touched the ground; 0 while one is down. For a landing effect that scales with the flight, or to ignore a hop that is too short to count. |
landing | readonly landing: VehicleLanding | The last touchdown (a flight that ended with a wheel meeting the ground), as of the last tick. All 0 before the first landing. |
drifting | readonly drifting: boolean | True while a drift is held: a vehicle with a drift block, its handbrake input down, steering above its minimum speed and angle. False always on a vehicle with no drift block. |
driftAngle | readonly driftAngle: number | The angle between where it points and where it travels while it drifts, radians, positive right like {@link Vehicle.steerAngle}; 0 when not drifting. |
driftCharge | readonly driftCharge: number | Seconds of boost-earning slide stored by the drift so far; 0 when not drifting. |
driftTier | readonly driftTier: number | The boost tier the charge has reached: 0 none, 1 the first of the vehicle's boostTiers, and so on. What a game turns into sparks and sounds. |
boosting | readonly boosting: boolean | True while a boost is still pushing: a mini-boost earned by a drift, or one {@link Vehicle.boost} started. |
slip | readonly slip: number | The slip angle the handling guard saw on the last tick: between where it points and where it travels, radians, positive right. 0 always on a vehicle with no handling block. |
offRoad | readonly offRoad: boolean | True while any wheel is on an offRoad surface (as of the last tick); false always on a vehicle with no offRoad block. |
recovering | readonly recovering: boolean | True for recovery.protectFor seconds after an automatic recovery (vehicle-recovered); false always on a vehicle with no recovery block. |
lastRecovery | readonly lastRecovery: { why: VehicleRecoveryWhy; at: number } | null | The last automatic recovery: why, and the world time it happened in seconds; null before any, and always on a vehicle with no recovery block. |
boost | boost(speed: number, seconds: number): boolean | Starts a boost: speed m/s on the top speed for seconds, with the drift block's boostThrust on the drive force (1.3 on a vehicle without a drift block), fading out over its last 30% as a drift's mini-boost does. Like that one it pushes while the throttle is held. Returns false, changing nothing, when a boost already running gives more speed than this one; a drift's own boost replaces it when cashed in. Both numbers must be finite and above 0, or it throws vehicle.boost-invalid. It boosts the wheels' drive: a watercraft's water thrust is not boosted yet, so on a boat it starts (and reads boosting) but does not push. ts // A boost pad: any kart driving over it gets 6 m/s more for 1.5 s. onTriggerEnter(ctx, other) { other.vehicle?.boost(6, 1.5) } |
wheels | readonly wheels: Wheel[] | Its wheels, in the vehicle's order: each one's {@link Wheel.drivenBy} is "vehicle". On an input: "script" vehicle a write to one takes it over until the next {@link Vehicle.drive}; on an auto one it changes nothing. |
water | readonly water: VehicleWater | null | What its water handling did on the last tick (a vehicle with a water block; null on every other): the hooks a wake, a bow spray or a splash sound reads. |
Example
The tested fixture fixtures/recipes/best-score-save/scenes/behaviors/best-score-save.ts.
import type { Behavior } from "@argus/api"
/**
* A best score that outlives the run, kept with `ctx.save`.
*
* Space scores, Return ends the round, and the best is saved before the round restarts.
*
* `ctx.state` is wiped by `ctx.session.restart()`, by `loadScene` and by quitting; `ctx.save` is
* not. So the best is read from the save in `init` — which runs again after every restart — and
* written there the moment it is beaten, never kept only in state.
*/
interface State {
score: number
best: number
}
/** One store per application, so a key is worth namespacing: 1–64 of `A-Z a-z 0-9 . _ -`. */
const BEST = "best-score.best"
export default {
init(ctx) {
ctx.state.score = 0
ctx.state.best = Number(ctx.save.get(BEST) ?? 0) || 0
ctx.hud.score(0)
ctx.hud.set("best", ctx.state.best)
},
update(ctx) {
if (ctx.input !== null && ctx.input.pressed("score")) {
ctx.state.score += 1
ctx.hud.score(ctx.state.score)
}
if (ctx.input !== null && ctx.input.pressed("finish")) {
if (ctx.state.score > ctx.state.best) {
ctx.state.best = ctx.state.score
ctx.save.set(BEST, ctx.state.best)
}
// Lands at the end of this tick; the save is written at the end of it too, first.
ctx.session.restart()
}
},
} satisfies Behavior<State>