# Race
La carrera: cuenta atrás, vueltas, Lakitu, posición y meta.
Vive en una entidad propia de la escena (`RaceDirector`). Los karts se apuntan
solos al despertar, así que la misma pieza sirve para el jugador, para las CPUs
y para quien entre después — y como el mundo es multijugador, la carrera es
una sola para todos los que están en ella.
Las reglas viven en `game.race` y no tocan el motor, así que se pueden
comprobar sin conducir.
## Fases
`idle` → `countdown` → `racing` → `finished`
Con `autoStart` (por defecto) la cuenta atrás se lanza sola en cuanto hay un
kart apuntado. La pantalla de selección la lanzará a mano con `startRace()`.
## Vueltas
La vuelta se reparte en **8 puertas de control**. Una vuelta solo cuenta si se
han pasado las ocho **en orden** y luego se cruza la meta, así que no se puede
hacer trampa yendo y viniendo por la línea. El circuito de tramo (el 10, la
Senda Arcoíris) no da vueltas: termina al cruzar la meta una vez.
## Lakitu
Si un kart se aleja más de 45 m del eje de la carretera y sigue así 1,6 s,
Lakitu se lo lleva de vuelta a la última puerta que pasó y le cuesta **3
monedas**. Salirse un poco al prado es parte del juego; perderse en el campo no.
## Public fields
| Campo | Qué es |
|---|---|
| `phase` | En qué punto está la carrera. |
| `countdown` | Segundos que quedan de la cuenta atrás. |
| `raceTime` | Segundos desde la salida. |
| `leaderLap` | Vuelta en la que va quien va primero. |
| `totalLaps` | Vueltas de la carrera. |
| `trackId` | Circuito en el que se corre. |
| `entryCount` | Cuántos karts hay apuntados. |
| `standings` | La clasificación: nombre, vuelta, progreso, posición y monedas. |
| `results` | El resultado final, en orden de llegada. |
| `lastEvent` | El último aviso: "VUELTA 2", "META", "LAKITU". |
## Methods
- `:register(kartId)` / `:unregister(kartId)` — los llama el propio kart.
- `:startRace()` — lanza la cuenta atrás.
- `:reset()` — vuelve al estado inicial sin lanzarla.
- `:kartStatus(kartId)` — vuelta, último tiempo, tiempo total y puesto.
## Usage
```lua
entity.spawn("RaceDirector").component.add("Race", { autoStart = true })
```
# items
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# game.core
El contrato compartido de **Mario Kart OrigoZero Ultimate**. Ninguna pieza del
juego escribe un número que pertenezca a la especificación: todos viven aquí.
`require("game.core")` desde cualquier parte del mundo.
## Qué trae
- **Conducción** — velocidad punta base, tiempo de aceleración (1,75 s de 0 a
máximo con un conjunto mediano), frenada, marcha atrás, giro y su caída a
alta velocidad.
- **Las 3 cilindradas** — 50cc / 100cc / 150cc con sus multiplicadores.
- **La K entera** — `DRIFT_CHARGE` (1 s / 3 s / 5 s), `DRIFT_BOOST`
(0,75 s / 1,5 s / 3 s), `HOP_CHARGE`, `HOP_JUMP_HEIGHT`, `HOP_BOOST`,
el saltito, el empuje lateral, el medio agarre cargando y el anti-spam.
- **Superficies** — carretera, tierra, arena, agua, barro, hielo, turbo y
fuera de pista, cada una con su velocidad, aceleración, agarre y rozamiento.
- **Monedas** — tope de 10, +0,5 % de velocidad cada una, miniturbo de 0,125 s,
reaparición a los 0,5 s, 3 monedas perdidas al resbalar.
- **Estados** — resbalón (1,5 s al 5 %), rayo (3,5 s al 33 %), tiritar (2 s),
explosión azul (0,66 s), truco (0,25 s, miniturbo de 0,75 s).
- **Estadísticas** — `Core.tuningFor(charStats, vehStats, kind, classId)`
convierte las 4 estadísticas de personaje y vehículo en conducción real, con
las bonificaciones de clase (ligero carga un 10 % más rápido, mediano un 5 %
y sus miniturbos un 5 % más, pesado sus miniturbos un 10 % más).
## Uso
```lua
local Core = require("game.core")
local tune = Core.tuningFor(char.stats, veh.stats, char.kind, "150cc")
-- tune.topSpeed, tune.accel, tune.turnRate, tune.chargeSpeed, tune.boostTimeMul
```
# achievements
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# game.track
Los circuitos de Mario Kart OrigoZero Ultimate, construidos en código en cada
carga: nada queda congelado en la escena, así que recargar el mundo deja el
mismo circuito.
```lua
local Track = require("game.track")
local inst = Track.build("pradera")
```
## Qué construye
- La carretera, como una fila de losas solapadas un 25 % para que no se vea
ninguna juntura.
- Un bordillo claro a cada lado.
- Nada más: el suelo, el cielo y el mar son de la escena.
## Consultas que da la instancia
| Llamada | Qué responde |
|---|---|
| `inst:nearest(p)` | La muestra más cercana: `index`, `t`, `arc`, `lateral` (metros a la derecha del eje), `along`, `planDistance`, `dir`, `right`. |
| `inst:surfaceAt(p)` | `"road"` si el punto cae dentro del ancho del asfalto; si no, la superficie del circuito (`"offroad"` por defecto). |
| `inst:startPose(i)` | Posición y rumbo (grados) de la muestra `i`, que es donde aparece un kart. |
| `inst:despawn()` | Quita el circuito del mundo. |
`Track.current` es el circuito activo. Construir uno nuevo quita el anterior.
## Añadir un circuito
Una entrada en `Track.defs`: su nombre, dificultad, ancho, colores, y una lista
de puntos de control que se cierran en bucle. La curva pasa por todos ellos y el
eje Y es la altura.
| Campo | Qué es |
|---|---|
| `points` | Puntos de control `{ x, y, z }` de la vuelta. |
| `width` | Ancho del asfalto en metros. |
| `surface` | Superficie de todo lo que no sea asfalto. |
| `walled` | Si lleva muros que impiden salirse. |
| `laps` | Vueltas de la carrera. |
| `roadTint` / `kerbTint` / `groundTint` | Colores. |
| `skyPreset` / `timeOfDay` | Cielo del circuito. |
| `sea` | Preset de océano, si el circuito tiene mar. |
# Kart
El modelo de conducción de Mario Kart OrigoZero Ultimate — y la K entera.
Vive en el cuerpo del kart (el `body` del `PlayerPrototype`). Attach it there
and every joining player drives their own.
## Controles
| Tecla | Qué hace |
|---|---|
| J | Acelerar. De 0 a velocidad máxima en 1,75 s con un conjunto mediano. |
| A / D | Girar a izquierda y derecha. |
| S / O | Frenar; por debajo de cierta velocidad, marcha atrás. |
| K | Saltar, derrapar y cargar el salto cargado. |
| L | Usar el objeto. |
## La K, entera
Un toque de K en el suelo salta. Si estás girando, el salto va **hacia el lado
contrario** al derrape que vas a hacer; si no, va recto.
Al caer derrapando empiezas a cargar, y cada nivel son chispas de las ruedas y
un miniturbo al soltar la K:
| Carga | Chispas | Miniturbo |
|---|---|---|
| 1 s | azules | 0,75 s |
| 3 s | naranjas | 1,5 s |
| 5 s | arcoíris | 3 s |
Al caer del salto recto, si sigues manteniendo la K y no giras, empiezas a
cargar un **salto cargado** — con la mitad de agarre. Las ruedas sueltan dos
remolinos de viento, y soltar la K da el salto de ese nivel:
| Carga | Viento | Salto | Miniturbo al aterrizar |
|---|---|---|---|
| 0 s | gris/blanco | — | — |
| 1 s | azul | pequeño | 0,5 s |
| 3 s | naranja | mediano | 1 s |
| 5 s | arcoíris | grande | 2 s |
Antes del azul no hay nada que soltar: ni miniturbo, ni salto.
Espamear la K no da miniturbos: cada salto reinicia la carga y entre salto y
salto hay un tiempo mínimo.
## Public fields
- `characterId`, `vehicleId`, `classId` — la selección.
- `isCpu`, `cpuSkill`, `gridIndex` — la parrilla.
- `speed`, `speedRatio`, `heading`, `coins`, `lap`, `lastLapTime`, `totalTime`,
`finished` — estado que leen la cámara, el HUD y la carrera.
- `drifting`, `driftSide`, `chargeMode`, `chargeLevel`, `chargeTime` — la carga.
- `boostTime`, `boostMul`, `boostKind` — el impulso en curso.
- `airborne`, `surface`, `squishTime`, `slipTime`, `starTime`, `controlLocked`,
`gone` — el resto de estados.
- `inputMap` — el mapa de controles que activa al arrancar.
## Methods
- `:setCombo(charId, vehicleId, classId, charStats, vehStats, kind)` — fija el
conjunto y recalcula la conducción.
- `:addCoins(n)` — suma o quita monedas (y da el miniturbo de 0,125 s al coger).
- `:applyBoost(duration, mul, kind)` — un impulso, sin acumular con el que ya lleve.
- `:slip()` — resbalón: 3 monedas y vueltas al 5 % durante 1,5 s.
- `:squish()` — rayo: encogido al 33 % durante 3,5 s.
- `:star()` — estrella: inmune, un 5 % más rápido, 10 s.
- `:rescueAt(index)` — Lakitu: vuelta al punto de control.
- `:isBoosting()`, `:isImmune()`.
## Lifecycle
- `awake()` — localiza las piezas visuales del kart y le pone las chispas.
- `start()` — activa su mapa de controles y se suscribe.
- `update(dt)` — toda la conducción.
- `onDestroy()` — suelta su reclamación del mapa de controles.
## Usage
```lua
body.component.add("Kart", { inputMap = "driveKart", heading = 0, classId = "150cc" })
```
# race
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.