# AGENTS.md: obby con IA en Roblox Studio

Construyes un **obby** completo **dentro de Roblox Studio** a través del MCP `roblox-studio`.
La persona que te habla no quiere tocar el editor: tú construyes, colocas, pruebas y arreglas.

## 1. Reglas que no se rompen

1. **Nada de Rojo.** Tampoco archivos `.lua` o `.luau` locales, Wally, Argon ni ningún sync.
   Todo el código vive dentro del place:
   - Scripts: créalos y edítalos con `multi_edit` (pasa `className` al crearlos). **Al crear uno nuevo, el
     primer edit lleva `old_string` vacío (`""`)**, que es lo que pone el contenido inicial.
   - Carpetas, partes y ajustes: `execute_luau` con `datamodel_type: "Edit"`.
2. **`--!strict` en la línea 1** de todos los scripts. Tipa los parámetros y lo que devuelven las funciones.
3. **Ningún script pasa de 200 líneas.** Si va a pasar, divídelo en ModuleScripts antes de escribirlo.
4. **El servidor manda.** El cliente solo pide cosas (por Remote) y pinta la UI. Monedas, precios,
   mejoras y efectos se calculan y se validan en el servidor. Nunca te fíes de un número que mande el cliente.
5. **No inventes API.** Si dudas de un método, una propiedad o un valor de `Enum`, consulta
   la documentación con la herramienta `skill` (`rbx-docs-search`) antes de escribirlo.
6. **Solo tocas lo tuyo:** las carpetas `Obby` de cada servicio. No borres ni muevas nada más del place.
7. **No uses `subagent` ni las herramientas `generate_*`.** Haz el trabajo directamente.
8. **`studio_id`:** pídelo una vez con `list_roblox_studios` y reutilízalo en todas las llamadas.
9. **No repitas una llamada que ya falló dos veces con el mismo error.** Cambia de enfoque o pregunta.
   Si `script_read` dice que un script no existe, es que hay que **crearlo** con `multi_edit`, no volver a leerlo.

## 2. KISS y SOLID, en Luau

- **KISS:** la solución más simple que funcione. Nada de clases, frameworks ni capas «por si acaso».
- **S (una responsabilidad):** un módulo hace una sola cosa. `CoinService` solo gestiona monedas.
- **O (abierto a extensión):** lo nuevo se añade con **datos y tags**, no editando los servicios.
  Una etapa nueva es una Part con el tag `Checkpoint`, y un power-up nuevo es una entrada en `Config` más un módulo.
- **L (intercambiables):** todos los power-ups cumplen el mismo contrato: una función `apply` que recibe el
  personaje y la definición del power-up, aplica el efecto y **devuelve la función que lo deshace**.
- **I (interfaces pequeñas):** cada módulo expone solo lo que otros usan.
- **D (inyección de dependencias):** cada servicio es un ModuleScript que devuelve una tabla con `init(deps)`,
  donde `deps` trae la configuración, los Remotes y los demás servicios. El `Main` hace `require` de todos los
  servicios que existan en `Services`, los mete en `deps.services` por nombre y **después** llama a `init` de
  cada uno, empezando por `PlayerData`. Un servicio usa otro a través de `deps.services`, nunca con `require`
  directo: así no hay dependencias circulares. Si falta un servicio, el `Main` lo salta sin error, para que el
  juego arranque desde el primer paso. Los controladores del cliente siguen el mismo patrón.

## 3. Estructura fija (créala con `execute_luau` antes de escribir scripts)

```
ReplicatedStorage.Obby
  Config        ModuleScript   niveles, etapas, precios, power-ups (solo datos)
  Types         ModuleScript   tipos compartidos (export type ...)
  Remotes       Folder         BuyUpgrade (RemoteFunction), Notify (RemoteEvent)
ServerScriptService.Obby
  Main          Script         crea deps y llama a init() de cada servicio
  Services      Folder         PlayerData, DataService, CharacterStats, CheckpointService,
                               KillBrickService, CoinService, UpgradeService, PowerUpService,
                               HazardService, LevelService      (ModuleScripts)
  PowerUps      Folder         Velocidad, Supersalto, Escudo    (ModuleScripts)
StarterPlayer.StarterPlayerScripts.Obby
  Main          LocalScript    arranca los controladores
  Controllers   Folder         HudController, ShopController, NotifyController (ModuleScripts)
Workspace.Obby
  Lobby, Nivel1, Nivel2, Nivel3, Decoracion   Folders
```

- **Un solo sitio escribe `WalkSpeed` y `JumpHeight`: `CharacterStats`.** Los calcula a partir de las
  mejoras y de los atributos de power-up del personaje. Así mejoras y power-ups no se pisan.
- La **UI se crea por código** en los controladores (ScreenGui con `Instance.new`), no a mano en StarterGui.
- Textos de la UI en español. Los identificadores de código, en inglés.

**Tags de CollectionService.** Todo lo que el mundo hace se declara con un tag y, si hace falta un dato, con un
atributo en la propia parte (una etapa es un tag más un número; un power-up, un tag más un nombre). Cada tag lo
atiende **un único servicio**, con `GetTagged` para lo que ya existe y `GetInstanceAddedSignal` para lo que se
añada después. El patrón completo y el vocabulario de tags están en la **skill `tags-y-servicios`**: cárgala con tu herramienta `skill`
cuando un prompt la nombre, y **usa los nombres tal cual, sin renombrarlos**, porque los pasos siguientes cuentan con ellos.

## 4. El mapa

- Todas las partes con `Anchored = true`. Las etapas van numeradas en orden y cada nivel continúa la numeración
  del anterior.
- **Lobby** en el Baseplate, alrededor de (0, 0, 0), con la tienda y el portal al primer nivel.
- Cada nivel avanza hacia +Z, empieza en la posición que le da su prompt y tiene debajo su propio lago de lava.
  Las medidas, la física del salto y los patrones de variedad están en la **skill `mapas-y-distancias`**; el aspecto
  (paletas, suelo, checkpoints, portales, luz y sonido), en `ambientacion-y-estetica`; la economía, en `economia-y-powerups`.
- **Los niveles no se pueden solapar.** Cada uno ocupa un tramo propio de Z, y el siguiente empieza al menos
  50 studs después de donde acaba el anterior. Si dos comparten el mismo trozo de Z, el jugador aparece dentro
  del otro nivel y sus checkpoints le devuelven la etapa vieja. Las medidas de la skill **no se recalculan
  ni se cambian**: ya cumplen esta regla y la de los saltos.
- **Saltos:** de 4 a 7 studs de borde a borde y como mucho 3 studs de subida. Así todo se puede pasar
  con WalkSpeed 16 y JumpHeight 7.2, sin mejoras. Las mejoras ayudan, pero no son obligatorias.
- **Nada de plataformas que muevan al jugador encima.** Si una Part anclada se mueve, el jugador resbala.
  Lo que se mueve es lava que hay que esquivar.

**Cómo construir un nivel sin errores:**
- **Vacía la carpeta del nivel antes de construir** (`ClearAllChildren`). Si no, cada reintento duplica plataformas
  encima de las anteriores y luego no se ve el fallo.
- **Genera las plataformas con un bucle**, no una a una. Decenas de plataformas y checkpoints escritos a mano es
  donde se cuelan las coordenadas mal copiadas.
- **Ancla cada parte al crearla**, antes de asignarle el `Parent`, o caerá al vacío en el primer Play.
- **Al terminar, devuelve las etapas creadas y la Z final.** El siguiente nivel no puede empezar antes de esa Z más 50.
- **Comprueba que el primer checkpoint de cada nivel esté dentro de su propio rango de Z.** Es la prueba más rápida
  de que los niveles no se solapan.

## 5. La Creator Store: decorar sin que nadie toque el editor

1. Busca con `search_asset`: `scope: "creator_store"`, `priceFilter: "free"`, `verifiedCreatorsOnly: true`
   y `maxResults: 5`. Elige por nombre y tipo `Model`.
2. Inserta con `insert_asset`, con `parentPath: "game.Workspace.Obby.Decoracion"` y `assetName`.
3. **Límpialo en cuanto lo insertes, antes de moverlo o escalarlo.** Los modelos gratis pueden traer scripts
   maliciosos (backdoors). Recorre **todos** los descendientes del modelo y destruye cualquier
   `LuaSourceContainer` (Script, LocalScript y ModuleScript, a cualquier profundidad). En la misma pasada,
   ancla todas las `BasePart`: los modelos de la tienda suelen venir sin anclar y se desmoronan al dar Play.
   Devuelve cuántos scripts borraste.
4. **Mídelo y colócalo por su caja, no por su pivote.** `GetBoundingBox` da el centro y el tamaño reales del modelo;
   el pivote (`GetPivot`) puede estar a metros de la malla, así que si lo colocas con `PivotTo` a la posición
   deseada, el modelo aparece desplazado, enterrado o flotando. Calcula la diferencia entre el pivote y el centro
   de la caja y compénsala al colocarlo. Para que apoye en el suelo, el centro de la caja va a la altura del suelo
   más la mitad del alto.
5. **Si hay que escalarlo, escala primero y mide después.** `ScaleTo` es relativo a `GetScale`, no absoluto, y
   la caja cambia al escalar: si colocas con la medida vieja, el modelo queda hundido o en el aire.
6. La decoración va **a los lados** del recorrido (al menos 8 studs en X) o lejos, nunca encima de una
   plataforma ni en la trayectoria de un salto. Si un modelo estorba, `CanCollide = false`.
7. Di cuántos scripts borraste en cada modelo. Si un modelo no se deja limpiar o no se ve, bórralo y busca otro.

## 6. Comprobar cada paso antes de darlo por terminado

`execute_luau` te devuelve lo que el código **retorna** (`return ...`), no lo que imprime con `print`.
Para leer un resultado, termina el código con `return`.

1. Arranca el juego con `start_stop_play` (`is_start: true`), espera 5 segundos con `sleep 5` (es el único
   comando de terminal permitido), lee `get_console_output` y para con `is_start: false`.
   **Pase lo que pase, antes de terminar tu respuesta deja Studio en modo Edit**: el siguiente paso lo necesita.
2. Si hay errores rojos, arréglalos y repite. **No digas que funciona sin haberlo probado.**
3. Si `start_stop_play` responde «Start play hasn't finished yet» dos veces seguidas, **para** y pide al
   usuario que pulse el botón rojo de Stop en Studio. No lo reintentes en bucle.
4. Revisa las reglas 2 y 3 con `execute_luau` en Edit: recorre los descendientes de `ServerScriptService`,
   `ReplicatedStorage` y `StarterPlayer` que cuelguen de una carpeta `Obby` y sean `LuaSourceContainer`; para cada
   uno cuenta las líneas de su `Source` (saltos de línea más uno) y comprueba que **empiece exactamente** por
   `--!strict`. Devuelve la lista de los que incumplen con su número de líneas, o un «OK» si no hay ninguno.
5. Termina cada paso con un **resumen de 5 líneas como máximo**: qué creaste, dónde, qué probaste y qué falta.

## 7. Trampas conocidas de Roblox (evítalas)

- `wait`, `spawn` y `delay` están obsoletos: usa `task.wait`, `task.spawn` y `task.delay`.
- `Touched` se dispara muchas veces: pon un debounce por jugador.
- Para saber qué jugador tocó una parte, busca el `Model` ancestro de la parte que tocó y pásaselo a
  `Players:GetPlayerFromCharacter`. Si no hay jugador, ignora el toque: también tocan las monedas y los NPC.
- Salto: usa `Humanoid.JumpHeight` (en los places nuevos `UseJumpPower` es `false`), no `JumpPower`.
- **DataStore falla en un place sin publicar.** Todas las llamadas van en `pcall` con 3 reintentos; si falla,
  `warn` una sola vez y el juego sigue sin guardar. **Si la carga falló, no guardes nunca a ese jugador**,
  o pisarías sus datos reales con ceros.
- En modo estricto, los hijos que se buscan por nombre salen como `Instance`: haz el cast
  (`:: IntValue`) o usa `FindFirstChild` con comprobación.
- Guardar el place no lo haces tú: al terminar, recuérdale al usuario que pulse **Archivo › Guardar**.
