# LostMagic: datos del juego para hacer tu propia versión

LostMagic (Taito, 2006, Nintendo DS) es un juego de táctica. Isaac, el protagonista, lanza hechizos dibujando runas en la pantalla táctil y pelea junto a los monstruos que recluta, en una historia cuyas decisiones llevan a cuatro finales.

Estos archivos tienen lo que se extrajo del cartucho del juego (versión de EE. UU.): los sprites y las animaciones de cada unidad, los retratos de los personajes, los mapas de batalla con dónde empieza cada enemigo, la historia completa con sus decisiones y la lógica que ordena las misiones, los textos en inglés, español, francés y alemán, la música, los efectos de sonido y el resto del arte. Úsalos para hacer una versión del juego en cualquier plataforma.

URL base: https://lostmagic-sitio.vercel.app/

Las rutas dentro de los JSON empiezan con `/` y son relativas a la URL base.

Para descargarlo todo de una vez: https://lostmagic-sitio.vercel.app/data/files.txt (una URL por línea).

## Archivos

| Archivo | Contenido |
|---------|-----------|
| `/data/units.md`, `/data/units.json` | Las 84 unidades (77 con nombre; el resto, apariencias sin nombre), cada una con el enlace a su detalle |
| `/data/units/<slug>.md`, `/data/units/<slug>.json` | Una unidad: sus estadísticas, hoja de sprites, el lugar exacto de cada cuadro y sus animaciones |
| `/data/levels.md`, `/data/levels.json` | Los niveles 1 a 50: experiencia, curvas de crecimiento de las estadísticas y capacidad del equipo |
| `/data/rules.md`, `/data/rules.json` | Los límites del juego: equipo, monstruos, objetos, niveles y experiencia |
| `/data/ai.md`, `/data/ai.json` | Movimiento e IA: rutas, búsqueda de objetivo y las órdenes de las unidades |
| `/data/duel.md`, `/data/duel.json` | El modo duelo: los equipos predefinidos y la puntuación por Wi-Fi |
| `/data/items.md`, `/data/items.json` | Los 8 objetos: qué mejoran, cuánto en cada nivel y en qué cofres aparecen |
| `/data/spells.md`, `/data/spells.json` | Los 396 hechizos: runas, MP, apuntado, potencia, estado, terreno y sus números |
| `/data/spell-families.md`, `/data/spell-families.json` | Las familias de hechizos parecidos y qué cambia dentro de cada una |
| `/data/spell-scenes.md`, `/data/spell-scenes.json` | Escenas: gráficos, paletas, cuadros y sonidos de los 396 hechizos; 90 unidades y 141 estados |
| `/data/runes.md`, `/data/runes.json` | Las 18 runas: los trazos que acepta el juego, dónde se aprenden y el MP de cada una |
| `/data/statuses.md`, `/data/statuses.json` | Los estados alterados: qué hacen, cuánto duran y qué hechizos los causan |
| `/data/combat.md`, `/data/combat.json` | Constantes de batalla: elementos opuestos, ataques normales, proyectiles y crecimiento de los niveles de elemento |
| `/data/characters.md`, `/data/characters.json` | Los 19 personajes de la historia y las expresiones de sus retratos |
| `/data/maps.md`, `/data/maps.json` | Los 55 mapas de batalla y sus 176 batallas: misiones y unidades al empezar |
| `/data/world-map.md`, `/data/world-map.json` | El mapa del mundo: 44 lugares, 50 caminos y lo que ofrece cada lugar en cada capítulo |
| `/juego/data.json` | La historia y su lógica: capítulos, misiones, escenas, batallas, banderas y finales |
| `/juego/textos/<en\|es\|fr\|de>.json` | Todos los textos de `/juego/data.json` en un idioma |
| `/data/audio.md`, `/data/audio.json` | 24 canciones y 147 efectos de sonido |
| `/data/gallery.md`, `/data/gallery.json` | 818 imágenes más: runas, hechizos y efectos, fondos, mapa del mundo, interfaz y arte sin usar |

## Convenciones

- Cada archivo de `/data/` viene en dos formatos con el mismo contenido: `.md`, en tablas, más fácil de leer y con menos tokens, y `.json`, para cargar los datos desde un programa. Los campos que se describen abajo son las claves del JSON; las tablas del Markdown usan los mismos nombres en sus columnas (`name.en`, `name.es` para los dos idiomas).
- Cada una de las dos pantallas de la DS mide 256×192 px. Las escenas de la historia se dibujan para la de arriba.
- Las coordenadas están en píxeles, desde la esquina superior izquierda, con la y creciendo hacia abajo.
- Las duraciones vienen en ticks de 1/60 s, la frecuencia de cuadros de la consola. Los campos `ms` son lo mismo, redondeado.
- El arte es pixel art: escálalo por números enteros, con muestreo del vecino más cercano, para que se vea nítido.
- Los nombres vienen en inglés y en español como `{ "en": …, "es": … }`. La historia tiene cuatro idiomas.
- Las imágenes son PNG; los GIF animados son solo vistas previas. El audio es AAC en `.m4a`: conviértelo si tu plataforma no reproduce AAC.

## Unidades y sus animaciones

`/data/units.json` lista todas las unidades: `uid` (el número que usan las batallas y la historia; `null` en las apariencias sin nombre), `slug`, `name`, `element` (`fire`, `wind`, `water`, `earth`, `dark`, `light` o `null`), un retrato (`portrait`), la hoja (`sheet`) y `details`, la URL con todos sus datos.

Cada unidad tiene una **hoja de sprites**: un PNG con todos los cuadros que usa. `sheet.cells` da el rectángulo exacto de cada cuadro en la hoja (`x`, `y`, `w`, `h`); su posición en la lista es el número del cuadro.

Una **animación** es una secuencia de cuadros que se repite, dibujada sobre un **lienzo** fijo de `width` × `height`:

- Cada elemento de `frames` dice qué cuadro de la hoja se muestra (`cell`), dónde va su esquina superior izquierda en el lienzo (`x`, `y`) y durante cuánto tiempo (`ticks`). Un mismo cuadro puede aparecer varias veces.
- `origin` es el punto de anclaje de la unidad en el lienzo. Queda fijo mientras los cuadros se mueven a su alrededor: así se desplaza el cuerpo en los saltos y los ataques. Coloca el lienzo de modo que `origin` caiga en la posición de la unidad.
- `action` y `facing` dicen cuándo la reproduce el juego: la acción de la unidad y hacia dónde mira (`down` de frente, `up` de espaldas, `left`, `right`; vacío si la acción no tiene dirección). `used` es `false` en las que están dibujadas pero el juego nunca reproduce.
- `preview` es la misma animación como GIF, para ver qué muestra. Los GIF guardan los tiempos en 1/100 s y unen los cuadros repetidos, así que usa `frames` para los tiempos.

Las acciones:

| `action` | Cuándo |
|---|---|
| `idle` | Quieta. Casi siempre es un solo cuadro, que está en la hoja pero no como animación |
| `walk` | Mientras se mueve |
| `attack` | Su ataque normal, cuerpo a cuerpo o a distancia |
| `chant` | Mientras se dibujan las runas (Isaac y los jefes) |
| `cast` | Al soltar el hechizo |
| `knocked_back` | La tumba un hechizo que derriba; después viene `get_up` |
| `get_up` | Se levanta después de caer |
| `sleep` | Un hechizo la duerme |
| `dance` | Isaac, con los hechizos de baile |
| `trapped` | La retiene una trampa de captura |
| `captured` | La captura funciona |
| `die` | Su vida llega a 0. Se reproduce una vez; medio segundo después la unidad se desvanece |
| `run`, `victory` | Dibujadas, sin uso (`used: false`) |

Si una unidad no tiene animación propia para una acción, se queda en su pose `idle`. Las que miran a la derecha son dibujos aparte, no un espejo hecho al reproducirlas.

```json
{
  "uid": 1,
  "slug": "isaac",
  "name": {
    "en": "Isaac",
    "es": "Isaac"
  },
  "element": null,
  "description": {
    "en": "A 14 year old boy. His face plainly displays his emotions.",
    "es": "Un joven. Su rostro muestra emociones."
  },
  "stats": {
    "hp": [
      300,
      450
    ],
    "mp": [
      30,
      130
    ],
    "attack": [
      80,
      80
    ],
    "defense": [
      25,
      38
    ],
    "magic": [
      30,
      130
    ],
    "magic_defense": [
      87,
      92
    ],
    "speed": [
      21,
      23
    ],
    "accuracy": [
      100,
      100
    ],
    "evasion": [
      0,
      30
    ]
  },
  "growth": "linear",
  "up_cost": 1,
  "squad_size": 1,
  "flying": false,
  "ranged": false,
  "attack_interval_ticks": 60,
  "attack_reach_px": 0,
  "radius_px": 8,
  "speed_px_per_tick": [
    0.65625,
    0.71875
  ],
  "attack_strike_ticks": 8,
  "attack_element": null,
  "projectile_type": null,
  "shot_start_px": null,
  "sight_px": 0,
  "keep_distance_px": 0,
  "portrait": {
    "url": "/unidades/isaac/retrato.png",
    "width": 125,
    "height": 125
  },
  "sheet": {
    "url": "/unidades/isaac/hoja.png",
    "width": 1012,
    "height": 156,
    "cells": [
      {
        "x": 6,
        "y": 10,
        "w": 32,
        "h": 32
      },
      "…"
    ]
  },
  "animations": [
    {
      "index": 1,
      "action": "walk",
      "facing": "down",
      "used": true,
      "preview": "/unidades/isaac/01.gif",
      "width": 35,
      "height": 34,
      "origin": {
        "x": 20,
        "y": 23
      },
      "frames": [
        {
          "cell": 4,
          "x": 3,
          "y": 2,
          "ticks": 15,
          "ms": 250
        },
        "…"
      ]
    },
    "…"
  ],
  "battles": [],
  "casts_spells": []
}
```

`battles` lista las batallas de la historia en las que participa la unidad.

Para dibujar el cuadro *i* de una animación: toma el rectángulo `sheet.cells[frames[i].cell]` y pégalo en el lienzo en `(frames[i].x, frames[i].y)`. Muéstralo durante `frames[i].ticks` / 60 segundos, pasa al siguiente y, al terminar, vuelve al primero.

### Estadísticas

Cada unidad con nombre trae, en `/data/units/<slug>.json`, sus números de las tablas del juego:

- `stats`: `hp` (vida), `mp` (maná), `attack`, `defense`, `magic`, `magic_defense`, `speed`, `accuracy` y `evasion`, cada una como `[nivel 1, nivel 50]`. En el nivel L vale `nivel1 + floor((nivel50 − nivel1) × factor / 128)`, donde `factor` es el valor de la curva `growth` de la unidad (`early`, `late` o `linear`) para el nivel L en `/data/levels.json`.
- `speed_px_per_tick`: cuánto camina por tick en los niveles 1 y 50; es `speed / 32`. Las que vuelan usan la misma velocidad.
- `flying`, `ranged` (su ataque normal es un proyectil), `attack_interval_ticks` (tiempo entre ataques), `attack_reach_px` (ataca cuando la separación entre los bordes de las dos unidades es menor que esto) y `radius_px` (su cuerpo es un círculo de ese radio).
- `up_cost` (lo que ocupa de la capacidad del equipo) y `squad_size` (cuántas unidades trae un escuadrón).
- `element` es el elemento de la unidad, y `description` el texto del juego sobre ella, en inglés y en español.

`/data/levels.json` tiene, por nivel: `exp_to_next` (la experiencia para subir al siguiente), los factores de las tres curvas (`growth`, sobre 128) y `lineup_up`, cuántos UP de unidades admite el equipo en ese nivel de Isaac.

## Movimiento e IA

`/data/ai.json` describe cómo se mueven las unidades y qué hacen por su cuenta. Sale de leer el código del juego (`evidence: static`); no se observó en un emulador.

- `movement`: las rutas son rectas (`route: straight`), sin búsqueda de caminos, con hasta `route_points_max` puntos; una unidad llega a un punto cuando está a menos de `point_reached_distance_px`. Un grupo enviado a un punto se reparte alrededor: cada unidad va al punto más su dirección desde el centro del grupo × (su radio + `group_spacing_extra_px`). Una unidad camina `velocidad / 32` px por tic, sin aceleración; los obstáculos y las demás unidades la empujan hacia afuera.
- Objetivo: cada `retarget_every_ticks` tics, en los estados `states_with_target_search`, busca enemigos a menos de `search_px` y elige el más cercano, el de más vida o el de menos vida (estas dos prefieren a los que están a su alcance).
- `orders`: cada orden (`order`, con las clases del juego en `classes`) fija un estado de decisión (`state`):
  - `wait` con modo 0 (estado 0) ataca sin perseguir a quien entre en su alcance + `attack_in_place_extra_px`; con otro modo (`state_other_mode`, 1) persigue dentro de su radio de visión.
  - `move` sigue la ruta sin atacar; `move_auto` ataca de paso sin salirse; `move_attack` persigue dentro de su radio de visión o sigue la ruta; `move_wait_auto` sigue la ruta, persigue y, detenida, espera.
  - `guard` (estados 9 y 10) vigila un punto: `guard` dice cuándo vuelve (`distance > sight`) y hasta dónde (`distance <= sight / 2`).
  - `escape` (11) huye en línea recta mientras la distancia entre centros es menor que la `keep_distance` de la unidad (`/data/units.json`).

  El radio de visión es el `sight` de cada unidad, y las misiones lo cambian.
- `unresolved_states`: los estados que existen pero de los que no se sabe todavía qué orden los produce.

## Duelo

`/data/duel.json` describe el modo duelo, en el que juegan dos personas. Sale de los datos y el código del juego (`DB/chrSelScnDat.cssd`, `DB/eloRate.elo`); no se observó en un emulador.

- `presets`: los 8 equipos predefinidos que se pueden elegir. Cada uno tiene `level` (el nivel del jugador, 40), `element_levels` (los seis niveles de elemento, que el juego limita a 1–50) y `squads`, tres escuadrones (`unit_id` de `/data/units.json`, `name`, `slug` y `level`). El cartucho no les da nombre: se numeran del 1 al 8.
- `rating`: la puntuación de las partidas por Wi-Fi, entre `min` y `max`. `rows` tiene, por fila, el límite de la diferencia (`difference`, puntuación propia − la del rival) y los puntos que suma una victoria (`win`), un empate (`draw`) o una derrota (`loss`). Con diferencia negativa se usa la primera fila cuyo límite es mayor o igual que la diferencia; con cualquier otra, la última fila cuyo límite es menor o igual. Después la puntuación se mantiene entre `min` y `max`. `elo_k` vale 50 porque las 53 filas coinciden, a menos de un punto, con el sistema Elo de K = 50: `K × (resultado − 1 / (1 + 10^(−diferencia/400)))`, con resultado 1, 0,5 o 0.

## Reglas del juego

`/data/rules.json` tiene los límites del juego, leídos de su código:

- `team`: `squads_max`, los escuadrones que entran con Isaac, nunca más que los puntos de inicio de escuadrón del mapa; `player_units_max`; `formation_px`, dónde empieza cada unidad de un escuadrón respecto de su punto de inicio (la y crece hacia abajo); `capacity_up_by_isaac_level`, los UP que admite el equipo en cada nivel de Isaac, desde el nivel 1.
- `roster`: `monsters_max`, los monstruos que se pueden guardar, y `captures_per_battle_max`.
- `items`: `inventory_max`, `found_level_max` (el nivel más alto de un objeto encontrado) y `found_per_battle_max`.
- `levels`: `level_max` de Isaac y los monstruos, `element_level_max` y `enemy_level_max`.
- `experience`: `per_kill_per_level` (cada unidad que muere da ese número × su nivel), `battle_max` y `level_1_to_max`.
- `battle`: `default_time_limit_s`, el límite cuando la misión no fija otro.
- `new_game_plus`: los niveles que suben los enemigos en cada capítulo (del 1 al 5, con el tope de `levels.enemy_level_max`) y los niveles extra de los objetos de los cofres (con el tope de `items.found_level_max`).
- `checkpoints`: cuántas misiones y escenas del mapa y cuántas batallas libres toman un punto de control al empezar.

Las reglas que no son un número:

- Isaac siempre está en el equipo y no lleva objetos. Cada escuadrón lleva un objeto, que afecta a todas sus unidades, y tiene tantas unidades como el `squad_size` de su especie.
- El UP solo se comprueba al armar el equipo, no en la batalla.
- Solo se capturan unidades enemigas comunes, no jefes ni aliados. Se suman al ganar, con su nivel de la batalla y 0 de experiencia. Se permiten especies repetidas en la lista y objetos repetidos en el inventario; con la lista llena, la nueva reemplaza a una que no esté en un escuadrón o se descarta.
- Da experiencia toda unidad del bando enemigo o aliada del mapa que muere, la mate quien la mate; las capturadas no dan nada. Al ganar, Isaac y cada escuadrón reciben el total, sin repartirlo.
- Una derrota o un empate descartan todo lo ganado en la batalla. Al continuar, la historia y las runas vuelven al punto de control, que se toma cuando una misión, escena o batalla libre empieza desde el mapa; nivel, monstruos y objetos se conservan, y la misión se repite desde el principio.
- Retirarse desde la pausa descarta la batalla y lleva al título, sin continuar.
- En la nueva partida+ Isaac conserva nivel, experiencia, niveles de elemento, runas, monstruos, equipo y objetos; la historia vuelve a empezar con batallas más difíciles.
- La pantalla de resultados, solo al ganar, muestra en orden: experiencia y niveles de elemento, objetos encontrados, monstruos capturados y hechizos nuevos. No hay rangos ni notas.
- No hay dinero ni tienda, ni un máximo de unidades en el campo o de hechizos activos. Isaac puede lanzar en cuanto termina la animación del hechizo anterior; el límite es el MP.

## Objetos

`/data/items.json` tiene los 8 objetos. Cada escuadrón lleva uno, que se aplica a todas sus unidades al empezar la batalla; Isaac no lleva ninguno.

`icon` es el ícono de 16 × 16 px que muestran los menús. Depende de la estadística que mejora el objeto, así que varios lo comparten. En la batalla no se dibuja ningún ícono de objeto: los cofres se ven siempre igual.

- `stat`: la estadística que cambia (`hp`, `speed`, `attack`, `interval`, `accuracy`, `evasion`, `defense` o `magic_defense`).
- `bonus_by_level`: el valor *v* en los niveles 1 a 10, sobre 128. Los cofres solo dan niveles 1 a `items.found_level_max` de `/data/rules.json`.
- `effect`, cómo se aplica *v* a la estadística *x* de la unidad:
  - `multiply`: `x + floor(x × v / 128)`. Con `hp`, la unidad además empieza con la vida llena.
  - `reduce`: `x − floor(x × v / 128)`, como mínimo 1.
  - `double`: `x × 2`, en cualquier nivel.
  - `gap_to_100`: `x + floor((100 − x) × v / 128)`.
- `chests`: los cofres de la historia que pueden dar el objeto: `battle` (el mismo número que en `/data/maps.json`), `map`, la posición `x`, `y` en píxeles del mapa, `chance` (0 a 1) y `levels`, la probabilidad de cada nivel.

Al cargar la batalla, el juego sortea el objeto y el nivel de cada cofre. El objeto es para la unidad del jugador que abre el cofre y se recibe al ganar. En la nueva partida+, el nivel sube 2, con el mismo tope.

## Hechizos y runas

### Runas

`/data/runes.json` tiene las 18 runas: `id` (1 a 18), `element`, `level` (1 a 3), su imagen, `spell` (el hechizo que lanza sola) y `learned` (las misiones donde se aprende). Isaac lanza un hechizo dibujando de 1 a 3 runas en orden mientras mantiene L (R con la opción para zurdos); soltar el botón termina el canto. Cada runa es un solo trazo: levantar el lápiz la cierra.

`templates` son los trazos que el juego acepta para cada runa: puntos en una cuadrícula de 0 a 63 (y hacia abajo), en el orden en que se dibujan. `from_dot` marca los que empiezan en el punto de la imagen de la runa; los demás son el mismo dibujo desde otro inicio o en el otro sentido.

Para reconocer un trazo:

1. Guarda un punto por fotograma mientras se toca la pantalla. Después del primero, guarda solo los que avanzan al menos 1 px en x respecto del último guardado: el juego no mira el avance en y. Un trazo de menos de 4 puntos cancela el canto.
2. Toma un cuadrado centrado en el trazo, de lado `max(ancho, alto)`, así que la proporción se conserva. Simplifícalo: conserva los dos extremos y, tramo a tramo, el punto más lejano de la cuerda si está a `max(1, 2 × lado / 64)` px o más, hasta 40 puntos. Pásalo a la cuadrícula de 0 a 63 con `(p − origen) × 64 / (lado + 1)` y quita los puntos repetidos seguidos.
3. Para cada punto k ≥ 1 calcula `angle[k]`, la dirección del segmento que llega a él, y `ratio[k]`, qué parte del largo total es ese segmento. El punto 0 toma la dirección del primer segmento.
4. Compara el trazo (I, con nI puntos) con cada plantilla (P, con nP puntos) recorriendo las dos líneas a la vez por su largo:

```
i = j = 1; remP = ratioP[1]; remI = ratioI[1]; score = 0
repeat nP + nI − 3 times:
  A = 1 − |angleP[j] − angleI[i]| / 180°        # angle difference wrapped to 0–180°
  if remP <= remI:
    remI −= remP
    q = I[i] if remI < ratioI[i] / 2 else I[i − 1]
    score += remP × A × (1 − manhattan(q, P[j]) / 128)
    j += 1; remP = ratioP[j]
  else:
    remP −= remI
    q = P[j] if remP < ratioP[j] / 2 else P[j − 1]
    score += remI × A × (1 − manhattan(I[i], q) / 128)
    i += 1; remI = ratioI[i]
```

5. Gana la plantilla de mayor puntuación (de 0 a 1); con empate, la primera. Si llega a `accept` (0,70), el trazo es su runa. La rotación no se tolera: unos 30° de giro ya quedan en el límite.

Una runa reconocida además tiene que estar aprendida, las runas dibujadas hasta ahí tienen que formar el comienzo de algún hechizo, no se puede pasar del límite de runas por hechizo (`runes_per_spell`: las misiones que permiten 2 y 3) y tiene que alcanzar el MP de esa runa. Si algo falla, el canto se cancela.

La **calidad** del hechizo es el promedio de las puntuaciones de sus runas. Si llega a `great` (0,85), se le suma 0,5: el factor va de 0,70–0,85 a 1,35–1,50.

### Hechizos

`/data/spells.json` tiene los 396 hechizos:

- `name`: el nombre del cartucho en inglés y en español. Los españoles suelen venir abreviados (ninguno pasa de 12 caracteres, por ejemplo «Torbell. f.»); el sitio los conserva tal cual, sin ampliarlos ni traducirlos, y muestra el inglés al lado.
- `runes`: sus runas, en orden. `element`: su elemento, que es el de su última runa: 294 de los 396 combinan runas de distintos elementos.
- `kind`: la familia (`cloud`, `wall`, `bullet`, `blast`, `falling_objects`, `aura`, `zone`, `unit_effect`, `object`, `dragon`). `variant`: el comportamiento concreto dentro de la familia.
- `mp_by_rank`: el costo en MP con el elemento en el rango 0 a 4. El rango es `floor((nivel del elemento − 1) / 10)`. El costo es la suma de sus runas: `mp_per_rune[posición][rango del elemento de esa runa][nivel de la runa − 1]` de `/data/runes.json`. Cada runa se cobra al dibujarla.
- `targeting`: cómo se apunta (`circle`: centro y radio; `line`: inicio y final; `fixed_circle` y `point`: un punto; `direction`: sale de Isaac hacia donde se suelta; `unit`: una unidad; `instant`: sin apuntar). `targets`: `enemies`, `allies` o `all`; es `null` en los tres que no tocan ninguna unidad (Transport y Flight mueven a quien los lanza, Restore solo cambia el suelo). `numbers.cast_range_px` es la distancia máxima desde Isaac.
- `effect_group`: solo en `unit_effect` (`null` en las demás familias), qué hace sobre las unidades. `heal` recupera vida o quita estados: la bandera `heal`, un estado de grupo `cure` o `regeneration`, o las variantes que pasan vida al propio bando (`life_thread`, `life_shine`, `light_transfer`). `boost` pone un estado a aliados y `weaken` a enemigos: todo estado que el juego lanza a aliados los ayuda y todo el que lanza a enemigos los perjudica. `other` es el resto: sin estado ni curación, o un estado a los dos bandos. Es la clasificación del filtro de la página de hechizos, no un campo del cartucho.
- `power`: con `power_scaled`, la potencia al lanzarlo es `floor(floor((power + magia de quien lo lanza) × factor de calidad) × (rango + 4) / 4)`; sin `power_scaled`, se usa `power` tal cual. Con la bandera `heal`, cura.
- `status`: el estado que puede causar: su registro (`id`), `group`, `chance` (con rango 0 y factor 1; se multiplica por el factor y por `(rango + 4) / 4`), `duration_ticks` y `power`.
- `terrain`: cómo cambia el terreno (`heat` vuelve magma el suelo, `harden` endurece el magma, `freeze` congela el agua, `dry` seca el pantano en arena, `swamp` vuelve pantano la arena movediza, `restore` lo restaura).
- `flags`: `piercing` atraviesa, `homing` persigue, `rapid` ráfaga, `spread` abanico, `lock_on` fija cada objetivo, `flying_only` y `walking_only` limitan a quién afecta, `knockback` empuja, `knockdown` derriba.
- `numbers`: los números de su familia, en tics de 1/60 s y en px.

### Familias de hechizos

`/data/spell-families.json` agrupa los 396 hechizos por el código del juego que los ejecuta: la clase y el subtipo de efecto de sus datos. El hechizo genérico sobre unidades (`direct`) se divide por función (`heal`, `damage` o `status-only`) y grupo de estado, y Prank, Joke y Trick van juntos como `tub`. Cada familia tiene `spells` (los `slug` de `/data/spells.json`), `same` (los campos iguales en todos, con su valor) y `differs` (los que cambian). Los campos son los de `/data/spells.json`: `numbers.<nombre>` y `status.<campo>` van aplanados, y `runes` es cuántas tiene. `power` solo se compara dentro de una familia, porque cada una lo usa distinto. `note` (EN/ES), cuando existe, dice cuándo conviene cada uno, solo donde la investigación lo sostiene; sale de leer el código, no de jugar. Las familias de un solo hechizo tienen `same` y `differs` vacíos.

Hechizos con reglas propias, por `variant`:

| `variant` | Regla |
|---|---|
| `bomb_man` | Pone una bomba en cada unidad objetivo, de los dos bandos. A los 180 tics, cada bomba viva golpea con la potencia (fuego) a toda unidad a 15 px, una vez por unidad, incluida la que la lleva |
| `dance` | Isaac camina al objetivo a 2 px/tic sin recibir daño (se rinde a los 180 tics). A 12 px baila 300 tics: golpea en los tics 9, 19 y 30 de cada ciclo de 43, con todo su ataque menos un cuarto de la defensa. Termina si el objetivo muere o se aleja más de 34 px |
| `light_transfer` | Daño de oscuridad; Isaac recupera el doble del daño causado, hasta 32 767 |
| `life_thread` / `life_shine` | Cura lo que al objetivo le falta, quitándolo en partes iguales a los demás aliados / a los enemigos, sin bajarlos de 1 de vida |
| `holy_pulse` | 600 tics: el bando de quien lo lanza gana 25/128 de ataque y el otro pierde 25/128 de defensa dentro del círculo. Relanzarlo reinicia el tiempo |
| `transport` | Quien lo lanza salta al punto si cabe (si no, falla); sin receptor ni impacto. Destello de salida al lanzarlo, destello de llegada a los 20 tics y cambio de posición a los 30; hasta entonces se le puede golpear donde estaba. Flight es el mismo salto con más alcance (250 px frente a 100) |
| `self_cannon` | Isaac vuela a 2 px/tic, hasta 240 tics; a 12 px, 620 de daño mágico de viento al objetivo (sin escalar) y 255 a Isaac |
| `free_lift` | Levanta al objetivo (no a Isaac ni a jefes) 64 tics; al caer, la potencia (viento) y quemadura fuerte a los enemigos a 40 px |
| `switch_life` | Intercambia la vida actual de Isaac y la del objetivo, cada una limitada a la máxima de quien la recibe |
| `gather` | Trae a cada aliado junto a Isaac, uno cada 6 tics, a ángulo al azar y a sus radios más 0–31 px |
| `death_gamble` | Elige al azar una unidad viva de cualquier bando tras 282 tics; recibe 2 × la potencia (fuego) y las demás a 50 px, la potencia |
| `prank`, `trick` | Una tina por objetivo; a los 20 tics quita exactamente 1 de vida, que cuenta como daño |
| `mine` | Cada mina dura 920 tics y estalla cuando un enemigo que camina entra en 14 px: potencia (tierra) y derribo a los enemigos a 14 px. Una mina nueva desarma las anteriores del bando |
| `capture_trap` | Atrapa al primer enemigo común en `trigger_radius_px`. Con la vida por debajo de `capture_hp_below` la captura siempre funciona; `intended_chance_*` no se usan. Una trampa nueva anula la anterior del bando |
| `elemental_wall` | Los postes no tienen vida; cada poste daña a quien lo toca como mucho una vez cada 120 tics |

## Escenas de hechizos

`/data/spell-scenes.json` (también `.md`) relaciona cada hechizo con sus gráficos y sonidos, sus jefes reales, las 90 apariencias de unidades y los 141 estados. La página de cada hechizo permite elegir lanzador, receptor, cuatro direcciones, distancia, calidad y resultado, repetir y activar el sonido. El receptor es aliado o enemigo según `targets`; los resultados y estados de ejemplo son selecciones del usuario, no tiradas de combate. Transport y Flight no tienen receptor ni resultado: su escena mueve al lanzador al destino.

- `assets[id]`: PNG, archivo CAN/NCER/NANR y paleta exacta; `cells` da rectángulos [x,y,w,h] de la hoja y `origins` su posición respecto al ancla. Un cuadro vacío sigue consumiendo su tiempo.
- `sequences[].frames`: [celda,tics,dx,dy,rotación en grados,escalaX,escalaY]. Incluye animaciones de un solo cuadro. `mode`: 1 una vez, 2 bucle, 3 ida y vuelta una vez, 4 ida y vuelta en bucle; `loop_start` es el cuadro inicial del bucle. `ticks` y `seconds` conservan la duración original.
- `units[].actions[acción][dirección]`: secuencia, `own` y `fallback`. Los monstruos sin conjuro o lanzamiento usan reposo. `launch_px` coloca el disparo. `boss_spells` identifica los hechizos que el jefe usa en batalla.
- `spells[].graphics`: función, hoja, secuencias, paleta, evidencia y confianza. `times_seconds` convierte los campos de tiempo del hechizo. Las distancias siguen en px; un proyectil tarda distancia/velocidad, redondeada al tic siguiente.
- `sounds`: audio, ancla (chant, stroke, rune, cast, release, shot, hit, status, death, vanish, capture, capture_close), desplazamiento en segundos y tics, condición y confianza. `timing_confidence` separa la atribución del sonido de su colocación de ejemplo. Rune/stroke se repiten por runa; hit por disparo. Calidad <0,85 usa KAKUNIN/INVOKE; ≥0,85 usa MENU_MAGIC/INVOKE2. El sonido se activa mediante interacción.
- `statuses[].overlay`, `popup`, `sounds` y `duration_seconds` describen el estado. `/data/statuses.json` también incluye este objeto como `visual`.
- Derribo usa solo la bandera 0x800: 40 tics en el suelo y 10 al levantarse; empuje, solo 0x400 y desacelera 30 tics. Derrota reproduce die, espera 30 tics, usa la nube 272 para monstruos y elimina después de 80. La captura resuelta usa `capture_timing`: cierre exitoso a los 2 tics, retirada de la unidad a los 33, o cierre fallido/liberación a los 60; las secuencias de libro 199/200 determinan el final, sin prolongarlo por la vida de la trampa armada.

Los muros (clase 1) crean estructuras en el terreno, sin receptor ni resultado de impacto. `structure_timing` conserva los 5 tics entre pilares; `targeting` distingue línea y círculo. Los estados elementales se aplican por contacto, no al colocar el muro. El visor permite ajustar la longitud de la línea.

La orientación de los proyectiles está en `graphics[].rotation`: el gráfico original apunta arriba y la ROM añade 90° al ángulo de movimiento. La estela hereda ese ángulo; oscuridad y luz giran 16,875°/tic. La fase inicial aleatoria se fija a 0 en la vista. El manifiesto tiene `revision` para evitar reutilizar datos de una exportación anterior en el navegador.

La composición de la escena es ilustrativa: no reproduce un combate completo. `preview` declara el ritmo de dibujo (0,8 s por runa) y las convenciones; `confidence`/`evidence` distinguen lo leído del código de lo probable. El seguimiento `extraccion/investigacion/visuals/SCENE_DISPATCH.md` confirma por código los sonidos de conjuro/trazo, el icono del byte 7 y las tablas de Fallthing. Las entradas cero omiten ese sonido. Las demás familias usan sus propias llamadas; `fall` y `object_hit` se repiten por objeto, con descenso de 20 tics como convención de la vista.

`visual_behavior` distingue efectos compartidos (Holy Pulse: icono 11; Life Thread/Life Shine: icono 7), movimiento de unidad (Self Cannon: `unit_motion`, 2 px/tic, separación de contacto 12 px) y estados condicionales (Betrayal/Mutiny/Uprising: `choices`). `caster_teleport` (Transport y Flight) marca el salto del lanzador: destello de salida al lanzar, destello de llegada a los `arrival_effect_ticks` (20) y cambio de posición a los `move_ticks` (30), sin receptor ni impacto. Sus probabilidades son condicionadas al éxito; el visor permite escoger una rama sin simular la tirada. `own_graphic_confirmed` se refiere al dispatch del informe original, no a la presencia de feedback compartido.

Quedan pendientes la composición visual de explosiones/dragones y el derribo en juego. La sincronización de fases, la colocación absoluta de la fase de Missile, los donantes de vida y el daño propio de Autocañón no se reconstruyen completamente. El seguimiento `extraccion/investigacion/visuals/SCENE_RENDERING.md` añade `dragon_assembly` (tabla de 14 índices, aparición 23+índice y escalas), `explosion_assembly` (núcleo, aro y partículas de carga) y `lock_timing` (8/16/24 tics). La trayectoria del dragón y las posiciones preliminares de Explosion siguen siendo ilustrativas. `sounds[].duration_ticks` corta el conjuro al lanzar y el trazo al terminar cada runa; los clips `*-sustain` conservan muestras/instrumentos de la ROM con una retención de 3 s elegida para la vista. Continuar reanuda las notas sostenidas con su desplazamiento y duración restante. Ninguno de estos hallazgos está verificado en emulador.

## Estados alterados

`/data/statuses.json` tiene los 141 registros de estado; el `status.id` de un hechizo apunta a uno. `duration_ticks` es cuánto dura (0: actúa una vez) y `caused_by`, los hechizos que lo causan. La intensidad de los que hacen daño o curan es la `status.power` del hechizo. `key` dice qué hace:

| `key` | Efecto |
|---|---|
| `stats` | Aplica `changes` mientras dura |
| `virus` | `changes`; las unidades a `spread_px` de una víctima original se contagian por el tiempo que le quede |
| `berserk` | `changes`; ataca a su objetivo e ignora las órdenes. Cancela `charm` |
| `burn`, `strong_burn` | Daño de fuego igual a la intensidad cada `every_ticks`, el primero tras `every_ticks`. No funciona en vados; el daño de agua la apaga; quita `freeze` y `bubble`. `strong_burn` reemplaza a `burn` |
| `poison` | Daño de oscuridad cada `every_ticks`, solo mientras la unidad se mueve o ataca |
| `vile_poison` | Daño de oscuridad cada `every_ticks` |
| `regeneration` | Cura la intensidad cada `every_ticks`, la primera al instante |
| `pulse` | Daño mágico de `element` igual a la intensidad al instante y tras `every_ticks` |
| `wall_contact` | Al tocar un poste: daño de `element` (`heal`: cura) igual a la intensidad del muro, como mucho una vez cada 120 tics por poste |
| `freeze`, `long_freeze` | No ataca ni se mueve; el daño de fuego la termina |
| `sleep`, `deep_sleep` | No ataca ni se mueve; cualquier daño la despierta |
| `bubble` | No ataca ni se mueve hasta recibir 2 golpes |
| `charm` | Pierde sus órdenes y pelea contra su propio bando. Cancela `berserk` |
| `terror` | Huye de la unidad que la amenaza |
| `forget` | Se queda quieta |
| `no_attack`, `no_move`, `no_damage` | No ataca / no se mueve / no recibe daño |
| `invincible` | No recibe daño, no ataca, no se mueve y no se la puede empujar |
| `hidden` | Se desvanece en 127 tics |
| `recoil` | Cada golpe que da le cuesta `share` × el daño que hace |
| `weakness`, `halving` | ×2 / ×½ el daño mágico de `element`; una anula a la otra |
| `cure` | Quita `removes` (`all`: todos los estados y cambios de estadísticas; `stats`: cambios de estadísticas, maldiciones y auras) y aplica `changes` si los tiene |
| `terrain` | Marca el terreno con `change` |
| `random` | Aplica uno de `options` según su `chance` |
| `none` | Sin efecto |

Cada cambio de `changes` tiene `stat` (`attack`, `defense`, `speed`, `magic`, `magic_defense`, `interval` = tiempo entre ataques, `accuracy`, `evasion`) y `mode`: `percent` → `stat × (1 + amount)`; `flat` → `stat + amount`; `gap` → `stat + (100 − stat) × amount`.

- Los estados no se acumulan: aplicar uno otra vez reinicia su tiempo. Los cambios de estadísticas, las auras y las maldiciones sí, y se multiplican.
- Cada fotograma las estadísticas se recalculan desde las de la unidad aplicando sus estados, del más nuevo al más viejo, y se limitan: ninguna baja de 0; ataque, defensa, velocidad, magia e `interval` llegan como mucho al doble de las de la unidad (`interval` al menos 1); la defensa mágica, a 99; la precisión y la evasión, a 150 más que las de la unidad.
- Isaac y los jefes son inmunes a `berserk`, `charm`, `terror` y `forget`.

## Combate

La batalla corre a 60 tics por segundo. `/data/combat.json` tiene las constantes del código; las de cada unidad están en `/data/units/<slug>.json`.

**Ataque normal.** `gap` = distancia entre centros − `radius_px` de las dos unidades.
- Empieza a golpear con `gap < attack_reach_px` y sigue mientras `gap < attack_reach_px + attack.reach_pad_px`. Con `attack_reach_px` 0 no ataca nunca; al perseguir se detiene a `attack.stop_gap_px`.
- Ritmo: espera `attack_interval_ticks`, reproduce su animación `attack`; el golpe o el disparo sale a los `attack_strike_ticks` de empezarla; al terminar la animación vuelve a esperar.
- Acierto cuerpo a cuerpo: `azar(0..100) + precisión − evasión ≥ 100`. A distancia: `azar(0..99) < (precisión − evasión) mod 256` (una diferencia negativa acierta siempre).
- Daño: `d = floor(ataque / 2) − floor(defensa / 4)`; si `d ≤ 0`, nada. Con `attack_element`: ×½ contra el mismo elemento, ×1,5 contra el opuesto (`opposite`). Los disparos hacen ×1,5 a las unidades que vuelan. Al final `floor(d × (128 + r) / 128)`, con `r` entero al azar entre −10 y 10.

**Disparos** (`projectile_type` de la unidad, `projectiles` de `/data/combat.json`): salen de `shot_start_px` según hacia dónde mira, en línea recta hacia donde estaba el centro del objetivo, a `speed_px_per_tick`, sin perseguirlo. Recorren como mucho `attack_reach_px + reach_pad_px`. Impactan a la unidad enemiga más cercana que esté a `shot_hit_radius_px` + su radio; no atraviesan. Los obstáculos altos los detienen; el agua y los pozos no. `on_hit`: `stick` se queda `stick_ticks` sobre el objetivo, `vanish` desaparece. Si fallan, rebotan y se desvanecen en `shot_fade_ticks`.

**Visión.** Cada `search_every_ticks` la unidad busca enemigos a menos de `max(sight_px, search_px)` y persigue mientras `gap < sight_px`. Los guiones de misión cambian `sight_px` a menudo.

**Daño de los hechizos.** Con la potencia al lanzarlo P (ver «Hechizos y runas»), sobre cada objetivo:
1. Elemento: sin elemento, P; mismo elemento que el objetivo, `−floor(P / 8)`; opuesto, `floor(3P / 2)`.
2. Si es mayor que 0, `× (100 − defensa mágica) / 100` (defensa mágica ≤ 99); si es 0 o menos, se divide entre 2 (el mismo elemento cura alrededor de P/16).
3. `× (128 + r) / 128`, con `r` al azar entre −10 y 10.
4. Si el objetivo tiene más estados `weakness` que `halving` de ese elemento, ×2; si tiene más `halving`, ×½.
Los hechizos con `heal` suman P sin elemento, defensa ni azar.

**Jefes.** Solo la unidad jefe de cada batalla lanza hechizos (`casts_spells` en su ficha), cuando su guion se lo ordena; no hay comprobación de alcance. Tiene su propio MP (su estadística `mp`), paga cada runa con rango 0 y su potencia es `power + magia`, sin calidad ni rango.

**Niveles de elemento de Isaac.** Empiezan en `growth.start`. Solo cambian en la pantalla de resultados de una batalla. Con `old` el nivel antes de la batalla, `c` los hechizos de ese elemento lanzados en ella (uno por hechizo, el elemento de su última runa; los cantos cancelados no cuentan) y `fav` el elemento lanzado estrictamente más veces (ninguno si hay empate):
- `old ≤ low_band_max`: +`low_band_many_gain` si `c ≥ many_casts`, +1 si `1 ≤ c < many_casts`; +1 más si es `fav`.
- `old ≤ mid_band_max`: +1 si `c ≥ many_casts`; +1 más si es `fav`.
- Si no: +1 solo si es `fav`, y entonces el elemento opuesto pierde 1 si su nivel era `decay_min` o más.
- Después, cada nivel se limita a `level_min`–`level_max`.
El nivel solo cambia el rango (`floor((nivel − 1) / 10)`), y el rango solo cambia el MP de las runas, la potencia (×(rango + 4) / 4) y la probabilidad de los estados (×(rango + 4) / 4).

## Personajes

`/data/characters.json` lista los 19 personajes que hablan en la historia, con su nombre (`name`), cuántas líneas tienen (`lines`) y `faces`: una imagen por expresión, en el orden del juego. Las escenas usan estos retratos a través de `/juego/data.json` (ver "Escenas").

## Mapas de batalla y batallas

`/data/maps.json` lista cada mapa de batalla: la imagen completa (`image`), el minimapa del juego (`minimap`) y sus batallas (`battles`). Una batalla tiene su número (`battle`), las misiones de la historia que la usan (`missions`) y las unidades que están en el campo al empezar (`units`):

- `uid` y `unit` (el slug en `/data/units.json`) dicen quién es.
- `x`, `y` es su posición en el mapa. Con el `origin` del sprite en ese punto, las unidades quedan sobre el suelo transitable del mapa.
- `level`, y `named`: `true` para los personajes de la historia (jefes y aliados), `false` para los monstruos comunes.

Cada misión de `missions` trae además `objective` (el texto que muestra el juego), `conditions` (cómo termina: `result` `victory`, `defeat`, `defeat_passed` —una derrota que cuenta como superada— o `draw`, cuando se cumple `trigger`: `no_enemies`, `boss_dies`, `isaac_dies`, `time` (`value` en segundos), `all_crystals`, `no_crystals`, `crystals_held` (`value` cristales), `isaac_reaches_area`, `boss_reaches_area`, `spell_cast` (`value` = id del hechizo), `chest_opened`, `capture`, `draw`…), `time_limit_s` (sin límite propio, 3600) y `music` (el `n` de la canción en `/data/audio.json`). Si la misión tiene varias batallas, la lista vale para todas.

`chests` son los cofres de la batalla: posición `x`, `y` y `odds`, lo que puede dar (`item` de `/data/items.json`, `chance` y la probabilidad de cada nivel en `levels`).

`terrain` es la colisión y el terreno del mapa, en px del mapa (las formas pueden salir del borde). No hay cuadrícula: las unidades se empujan fuera de las formas cada fotograma.

- `boxes` (centro `x`, `y` ± `half_width`, `half_height`) y `circles` (`x`, `y`, `radius`), cada una con su `kind`:
  - `high_obstacle`: detiene a las unidades que caminan, a las que vuelan y a los disparos. `low_obstacle`: solo a las que caminan.
  - Zonas, solo para unidades que caminan (las que vuelan nunca se ven afectadas): `magma` y `damage_floor` hacen 50 de daño cada 60 tics (fuego y sin elemento); `ground` igual, solo si un hechizo lo calentó; `ford` mitad de velocidad salvo congelado, apaga y bloquea las quemaduras; `ice` nada, mitad de velocidad si un hechizo lo derrite; `bog` mitad salvo seco; `sand` nada, mitad si se vuelve pantano; `quicksand` empuja 0,25 px por tic hacia `push`.
  - El daño de 50 se multiplica por (118 + azar(0..20)) / 128; una unidad del mismo elemento se cura 6 y una del opuesto recibe 75. Las de agua ignoran la lentitud del vado y del hielo derretido; las de tierra, la de ciénaga, arena y pantano y el empuje.
  - `event_area` (con su número `area`): las misiones preguntan si hay unidades dentro; no detiene a nadie.
- `squad_starts`: donde aparecen los escuadrones del jugador. Las unidades de un escuadrón se colocan en ese punto más `team.formation_px` de `/data/rules.json`.
- Los hechizos de terreno (`terrain` en `/data/spells.json`) cambian cómo actúan las zonas dentro de un círculo durante 20 s; no cambian las formas.

## Mapa del mundo

`/data/world-map.json` describe el mapa por el que viaja Isaac entre misiones. `picture` es `/juego/mundo.png`; las posiciones están en píxeles de esa imagen.

- `places`: los 44 lugares, con `id`, `x`, `y` y `neighbors`, los lugares unidos a él por un camino. `region` es su posición en `regions`: el nombre y la descripción que muestra el juego (`name`, `description`, en `en`, `es`, `fr` y `de`); varios lugares comparten región.
- `availability_group` (0 a 15) es el interruptor que habilita el lugar, y `unlock_flag` la bandera de `/juego/data.json` que lo enciende. Según el código, un lugar se habilita cuando su bandera está encendida, y un camino cuando lo están sus dos lugares.
- `paths`: los 50 caminos, como pares de `id`. Son rectos y se recorren en los dos sentidos. Isaac solo va a un lugar unido por un camino, y cada tramo dura `movement.leg_frames` (60) cuadros, sea largo o corto.
- `offers`: las misiones y pueblos que el mapa ofrece en cada lugar.
  - `chapter` es el capítulo de `/juego/data.json` (`qc01` a `qc06`; `sc01` a `sc03` para los secundarios) y `step`, el paso dentro del capítulo. `optional` es `true` si completarla no cierra el paso.
  - `objective` es el texto de sus batallas; vacío si solo tiene escenas. `scenes` son ids de `/juego/data.json` y `battles` trae `battle` y `map` de `/data/maps.json` (`null` si la batalla no tiene página de mapa).
  - Un mismo guion puede aparecer en dos lugares: el juego lo ofrece en uno u otro según una condición.
- `free_battles`: las batallas libres que el juego registra en cada lugar, con su capítulo.
- Al llegar a un lugar con algo, empieza, y el juego guarda un punto de control.
- `markers`: los tres marcadores del mapa. El rojo marca un lugar donde se ofrece una misión o un pueblo; el azul, otro tipo de evento sin identificar; el verde, no se sabe qué lo muestra.
- `confidence` dice cómo se sabe cada parte: `rom` sale de los datos del juego; `code`, de leer su código, sin comprobarlo en una partida.

## La historia: `/juego/data.json`

Este archivo es la lógica de la historia, leída de sus scripts: qué misiones están disponibles, qué ocurre en cada una, qué cambian las respuestas del jugador y a qué final llevan. Las rutas dentro de él son relativas a `/juego/`. Los textos están en `/juego/textos/<idioma>.json`, con los mismos ids.

### Estado de la partida

Una partida son cuatro cosas, todas vacías al empezar:

- **Banderas**: interruptores numerados, encendidos o apagados.
- **Contadores**: valores numerados de 0 a 127. El contador `rules.compassion` es el que decide entre dos finales.
- **Respuestas**: para cada escena, la opción elegida en cada punto de decisión.
- **Completadas**: las misiones terminadas.

### Condiciones

Muchas entradas llevan una lista de condiciones, y todas deben cumplirse. Cada una es `[negate, test]` (si `negate` es `true`, la prueba debe fallar):

| Prueba | Se cumple cuando |
|--------|------------------|
| `["flag", n]` | La bandera n está encendida |
| `["counter", n, op, k]` | El contador n comparado con k (`<`, `<=`, `>`, `>=`, `==`, `!=`) da verdadero |
| `["answer", scene, slot, option]` | En esa escena se eligió esa opción en ese punto de decisión |
| `["done", mission]` | Esa misión está completada |
| `["result", k]` | La última batalla de la misión terminó por la condición k: 2, no queda ningún enemigo vivo; 3, murió el jefe. Las dos son victorias |

### Capítulos y misiones

`chapters` lista los capítulos en orden: `qc01` a `qc06` para la historia principal y `sc01` a `sc03` para las misiones secundarias. Cada capítulo es una lista de **pasos** en el mapa del mundo; cada paso ofrece misiones como `[mission, offer, conditions]` y tiene `flags` que se encienden al terminarlo:

- Solo se ofrecen las misiones cuyas condiciones se cumplen.
- `offer` 159 marca una misión opcional. Un paso termina cuando se completa una de sus otras misiones (o cualquiera, si todas son opcionales); entonces se encienden sus `flags` y se abre el paso siguiente.
- Si no queda ningún paso abierto (falta encontrar algo), el último paso que ofreció misiones sigue abierto para que sus lugares se puedan volver a visitar.
- El capítulo actual es el primer `qc0N` cuya bandera en `rules.chapterFlags` (una por capítulo, del 1 al 4) está apagada. Con las cuatro encendidas, la historia sigue en `qc06` si la bandera `rules.route6Flag` está encendida, y en `qc05` si no. Los capítulos secundarios transcurren al mismo tiempo.

`missions` describe cada misión: `kind` (`m` batalla, `t` pueblo, `r` batalla libre) y una línea de tiempo (`timeline`) que se recorre en orden, saltando las entradas cuyas condiciones (el último elemento) no se cumplen:

| Entrada | Efecto |
|---------|--------|
| `["scene", id, how, conditions]` | Reproduce esa escena; `how` es 32 para una escena sola, y 29 o 31 para una que se muestra en la batalla siguiente |
| `["battle", id, conditions]` | Juega esa batalla |
| `["flag", n, value, conditions]` | Pone la bandera n en value (1 es encendida) |
| `["counter", n, delta, conditions]` | Suma delta al contador n, sin salir del rango de 0 a 127 |

Al final, la misión cuenta como completada. Los nombres de las misiones y de los capítulos están en los archivos de textos (`missions`, `chapters`).

### Escenas

`scenes[id].blocks` tiene los bloques de la escena; empieza en `main` y ejecuta sus elementos en orden:

| Elemento | Significado |
|----------|-------------|
| `["s", background, cameraX, cameraY, cast]` | Lo que muestra la pantalla de arriba desde ese momento |
| `["l", speaker, line]` | Una línea de diálogo: `names[speaker]` dice `scenes[id].lines[line]` |
| `["c", slot, [[line, block], …]]` | El jugador elige: el texto de cada opción es `scenes[id].lines[line]`. Guarda la posición de la opción como respuesta de ese punto de decisión y sigue en su bloque |
| `["n", block]` | Sigue en ese bloque |

La escena termina cuando un bloque se queda sin elementos. Las líneas conservan los saltos de línea del juego (`\n`). Isaac es el nombre por defecto del protagonista. `[L]`, `[B]` y `[+Control Pad]` (con las palabras de cada idioma) representan los botones de la consola.

Para dibujar la pantalla de arriba (256×192):

- **Fondo**: `bgs[background]` es `[picture, width, height]`. Muestra la parte que empieza en `x = clamp(-cameraX, 0, width - 256)`, `y = clamp(-cameraY, 0, height - 192)`.
- **Personajes**: cada `[actor, x, y, expression, mirror]` de `cast` es un retrato. `portraits["<actor>_<expression>"]` es `[picture, dx, dy, width, height]`: su esquina superior izquierda va en `(x + dx, 160 + y + dy)`, reflejada en horizontal cuando `mirror` es 1. `names[actor]` dice quién es.

### Finales

Cuando una misión enciende la bandera `endings.gameOverFlag`, el juego termina. La escena final es la que va con la primera bandera encendida de `endings.byFlag` (`[flag, scene]`), o `endings.default` si ninguna lo está. Sus títulos están en los archivos de textos (`endings`).

### Batallas en la historia

`battles[id]` es una batalla de la historia: `map` (su imagen), `size` y `units` como `[uid, x, y, level, character]` (`character` no es 0 en los personajes de la historia). Las mismas batallas, con nombres, están en `/data/maps.json`.

### Archivos de textos

`/juego/textos/<idioma>.json` tiene, para ese idioma: `names` (quién habla, por número), `scenes` (el `title` y las `lines` de cada escena), `missions`, `chapters`, `endings` y `units` (los nombres de las unidades por `uid`). Su `ui` tiene las etiquetas que usa la novela del sitio; puedes ignorarlo.

## Audio

`/data/audio.json` tiene `music` (`code` es el nombre de la canción en el juego) y `effects`, agrupados por `category`. Las canciones con bucle están grabadas tocando el bucle dos veces y desvaneciéndose al final. En el juego, una canción con `loop_end_s` vuelve a `loop_start_s` al llegar a `loop_end_s` y suena para siempre; una con `end_s` suena una vez. `missions` y `scenes` cuentan las misiones y escenas que la usan (la de cada misión está en `/data/maps.json`), y `screen` es la pantalla fija que la usa: `world_map`, `mission_complete`, `victory` y `defeat` (al terminar una batalla), `game_over`, `title` o `ending`.

`sound_events` dice qué hace sonar los efectos de los menús, el mapa del mundo, la pantalla de resultados, los cofres y los cristales de maná. Sale de leer el código del juego (`evidence: static`): cada lugar que reproduce un sonido y el número que carga; no se observó en un emulador. El juego reproduce el sonido número N como la entrada N − 1 del archivo de sonidos elegido. Normalmente es `SEQ_SE`, la categoría «Juego» de `effects`.
- `events`: `group` (`menu`, `world_map`, `results`, `chest`, `crystal`), `event`, `sounds` (`id`, `name`, `url`) y `calls`, los lugares del código que lo hacen sonar. En los menús, `cursor`, `confirm` y `cancel` están identificados; `other` junta el resto de los sonidos de menú, de los que no se trazó qué acción los dispara en cada pantalla. En el mapa: `move` (un tramo de camino), `enter_event` (llegar a una escena o un pueblo) y `enter_battle`. En resultados: `exp_count` (cada paso del conteo de experiencia) y `level_up`. Cofre: `open` e `item` (el objeto obtenido). Cristal: `captured` y `lost`.
- `download`: mientras está abierta la pantalla Download del modo duelo, el juego usa otro archivo, `SEQ_SE_CURSORS`. En él, los números 2, 3, 4 y 108 suenan con sus propias versiones (`replaces` es el sonido normal de ese número). `url` es `null` si el sitio no tiene ese clip.

## Otras imágenes

`/data/gallery.json` agrupa el resto del arte por sección: runas (la forma de cada una, como imagen), hechizos y efectos de batalla, fondos, el mapa del mundo y sus marcadores, la interfaz, y arte sin usar o de prueba. `/juego/mundo.png` es el mapa del mundo.

## Lo que no está en los datos

Tu versión tiene que definir esto:

- Qué órdenes producen los estados 6, 7, 8 y 12 de la IA (`unresolved_states` de `/data/ai.json`) y cómo se ve la IA en una batalla: lo publicado sale del código, no se observó en un emulador.

Los gráficos, los textos y la música pertenecen a Taito y Ubisoft.
