Estas páginas se generan a partir de la documentación interna del proyecto. Las referencias a ficheros de código y a offsets del binario se conservan como procedencia —para que se vea de dónde sale cada afirmación—, pero todavía no se pueden seguir: el motor aún no está publicado. Cuando lo esté, pasarán a ser enlaces.
4 · El panel de depuración
El drawer de QA completo, sección a sección.
Cómo abrirlo: tecla `
`(backquote) o F4, o ?debug=1en la URL. Y desde ⚙ → «Debug (QA)» → «Abrir menú debug». Exponewindow.__u5debug` para consola y e2e.Sólo DEV. La fila del menú SISTEMA y el atajo se anuncian únicamente cuando la dependencia
openDebugexiste: en las pieles fiel/shader de producción no se le dice a nadie que este panel está ahí.Capturas nuevas generadas para este documento el 04-08-2026 contra dev server propio (
:5271), sobre . Viven endocs/verdicts/debug-drawer/.
El panel entero

Un drawer lateral derecho de 360 px, en DOM puro, con buscador de campos arriba y un acordeón de secciones debajo.
Censo, medido en vivo el 04-08-2026
| # | Sección | Campos |
|---|---|---|
| 0 | Atajos | 3 |
| 1 | Teletransporte | 7 |
| 2 | Party | 23 |
| 3 | Recursos | 9 |
| 4 | Mundo / Reloj | 15 |
| 5 | Trama / Quest | 45 |
| 6 | Transportes | 7 |
| 7 | NPCs (dead / met) | 65 |
| 8 | Mazmorra · salas despejadas | 17 |
| 9 | Moonstones | 32 |
| 10 | Inventario · Reactivos | 8 |
| 11 | Inventario · Equipo (stock) | 48 |
| 12 | Inventario · Hechizos | 48 |
| 13 | Inventario · Pociones / Pergaminos | 16 |
| Total | 14 secciones · 343 campos |
Las cuatro reglas de arquitectura
Antes del recorrido, porque explican casi todo lo que se ve.
1 · Cero-rand, por construcción
Toda escritura pasa por una única fachada (debug/debugApi.ts) que escribe directo en el estado vivo —la misma struct que se persiste al save nativo— y nunca por Intents ni por nada que consuma el stream del RNG. Cada método es un puñado de escrituras planas más un refresh().
Por qué importa: si el panel consumiera tiradas, usarlo cambiaría la partida de forma invisible, y cualquier careo contra el DOS quedaría contaminado. La invariante está aseverada por un test (debug-api.test.ts) que comprueba que liveSeed() no se mueve tras ninguna operación.
Incluso el teletransporte respeta la regla: replica la receta del deep-link fijando position y, en mapas pequeños, enterMap + hydrateInteriorObjects — los dos deterministas.
2 · El registro es declarativo; el panel no sabe de nada
registry.ts declara qué se puede editar ({label, get, set, widget}); panel.ts lo pinta. Añadir un campo nuevo es una entrada más en el registro, sin tocar el panel.
3 · No estorba al juego
El canvas sigue jugable con el drawer abierto, y el panel no captura las teclas del juego salvo cuando uno de sus inputs tiene el foco (stopPropagation sólo para eventos originados en el panel). El refresco es por campo: se re-lee get() y se actualiza el control sin reconstruir el DOM, de modo que el mapa de teletransporte y el foco se preservan.
4 · Ningún tope está inventado
Regla explícita del encargo original. Cada límite se deriva del tipo de campo del SAVED.GAM y del cap que el juego aplica, o de una fórmula ya derivada del binario, y se cita en la constante:
| Tope | Valor | De dónde sale |
|---|---|---|
| Oro / comida / experiencia | 9999 | campos u16, pero el juego los satura a CAP_WORD (add_word_capped, kernel 0x9C84) |
| Llaves, gemas, antorchas… | 99 | campos u8, pero el modelo trata las cantidades como valor de 2 dígitos |
| Karma | 99 | u8 en 0x2e2, 2 dígitos en Ztats |
| FUE / DES / INT | 30, no 99 | ⬅️ ver abajo |
El 30 es el detalle bonito. El planificador de turnos y los umbrales del binario operan como CONST − stat en byte sin signo: un atributo por encima de 0x23 (35) wrapea. Con DES 99 el grupo queda inmóvil e intocable — el juego «funciona», y está roto. Así que el tope del panel es 30: rápido de verdad y dentro del régimen. La etiqueta del campo lo lleva escrito, y la derivación está en audit-byte-wrap.
Es el ejemplo perfecto de por qué un editor de estado necesita conocer el binario: un control con max=99 habría sido «correcto» según el tipo de dato y habría producido un bug irreproducible.
Sección 0 · Atajos

Tres botones de un clic, arriba del todo porque son lo que más se usa:
| Botón | Qué hace |
|---|---|
| Maximizar todo | Party (HP/MP/stats/nivel/estado) + recursos + inventario a sus topes + todos los ítems especiales (garfio, regalía de LB, esquirlas, catalejo, sextante, reloj, insignia, caja, HMS Cape) |
| Mejor equipo para todos | Equipa a cada miembro con el mejor ítem por ranura, derivado de las tablas de ataque/defensa de los datos — no de una lista escrita a mano — respetando la regla de dos manos |
| Party completo al máximo | Llena el grupo con miembros reales del roster (hasta 6) y aplica los dos anteriores |
Los tres son lógica pura en debug/shortcuts.ts (sin mutar el Game, sin DOM), y la fachada los aplica. Todos cero-rand.
Verificación existente: docs/verdicts/debug-maxall/.
| Esquirlas y regalía | Insignia y especiales |
|---|---|
![]() | ![]() |
Sección 1 · Teletransporte

Dos vías: los desplegables (localización + planta, o mazmorra) y —la buena— el selector de mapa.
El selector de mapa

Modal grande y centrado, porque embutir un mapa en un drawer de 360 px no es usable. Tres pestañas:
- Overworld — 256×256 con zoom (rueda), pan (arrastre) y doble clic para acercar; capa Britannia / Underworld.
- Ciudades y castillos — retícula 32×32, con selector de planta si la localización tiene varias.
- Mazmorras — la planta completa 8×8, sin niebla (es una herramienta de debug), con el lenguaje visual de la gema: muro = bloque blanco, sala = caja amarilla, escalera = H. Ocho pestañas de planta.
Un clic teletransporta a esa celda exacta. También hay entrada numérica x,y + Enter. Al pasar el ratón se ve (x,y) y el nombre del tile, y en mazmorra el subtipo (p. ej. «Room #8»). Recuerda el último destino.
El detalle que decide su fiabilidad
Cada mapa se renderiza del dato VIVO del core, nunca de JSONs rancios. La retícula de ciudad sale de getActiveMap(world, loc, floor).tileAt — la misma fuente que usa el juego para decidir dónde puedes pisar. Un selector alimentado por un JSON exportado sería un mapa de otra versión del mundo, y sus errores parecerían bugs del juego.
Dos decisiones de UX
- Si el destino es muro o impasable, avisa pero permite: es debug, y a veces quieres aterrizar dentro de una pared.
- ESC cierra el modal sin llegar al juego (captura en la raíz), salvo dentro de sus propios inputs, donde hay que poder teclear.
Y la única acción marcada como peligrosa
La sección tiene un botón «Entrar mazmorra (flujo real)» con badge rojo:
«Entrada REAL de la mazmorra — puede consumir RNG del stream (no cero-rand).»
Es la excepción declarada a la regla 1, y el panel la marca visualmente en vez de esconderla. Un editor de estado que no distingue «escribir un byte» de «ejecutar el flujo del juego» es un editor en el que no se puede confiar para preparar un careo.
Capturas del teletransporte a los cinco tipos de destino: docs/verdicts/debug-teleport/ (01-overworld, 02-overworld-zoom, 03-underworld, 04-town, 05-dungeon) y del tileado y el arreglo de la gema en docs/verdicts/debug-picker-tiling/ y docs/verdicts/debug-picker-gemfix/.
| Antes del fix de la gema | Después |
|---|---|
![]() | ![]() |
Sección 2 · Party (23 campos)

Selector de personaje y, por miembro: nombre, género (0x0B/0x0C), clase (las nueve letras de "AMBFDTPRS"), estado (G/P/C/S/D), HP, HP máx, MP, nivel, experiencia, FUE/DES/INT, partyStatus, meses en la posada y todas las ranuras de equipo.
Las etiquetas llevan la advertencia pegada: «Fuerza (máx 30 — >35 wrapea el scheduler, audit-byte-wrap)». La derivación viaja con el control, no en un documento aparte que nadie abrirá.
También: tamaño de grupo (1..6) y personaje activo.
Sección 3 · Recursos (9 campos)

Oro, comida, llaves, gemas, antorchas, llaves de calavera, alfombras mágicas, karma y los demás contadores globales. Cada uno con su tope derivado (§«Ningún tope está inventado»).
Sección 4 · Mundo / Reloj (15 campos)

Año, mes (con los seis nombres reales: Deep Winter, Awakening, Time of Sowing, Season of Sun, Time of Harvest, Fruitful Winter), día, hora, minuto, viento (Calm/N/S/E/O), transporte, y los campos de runtime que el save persiste:
- turnos desde el inicio, minutos de antorcha,
- efecto temporal (
timeSpell) y sus turnos, - minutos de luz (
lightSpell), prevHour(snapshot),- fases de Felucca y Trammel latcheadas.
Que las dos fases lunares latcheadas sean editables no es un capricho: son las que determinan a dónde lleva una moongate, y sin ellas no se puede montar el escenario de un careo de viaje lunar.
Sección 5 · Trama / Quest (45 campos)
Palabras de poder pronunciadas (×8), shadowlords muertos (×3), in-doom, game-won, Blackthorn… y toda clave viva de questFlags, en crudo, incluidas las que no se saben derivar (marcadas «sin derivar»).
Enumeración total, no curada — mandato explícito del usuario. La diferencia importa: una lista curada sólo enseña las banderas que alguien ya entendía, que son justo las que menos falta hace inspeccionar.
Contrapartida: se puede escribir una bandera que nadie entiende
El precio de enumerarlo todo es que el panel ofrece controles editables sobre claves sin derivar. Se pueden ver —que es el objetivo— pero también se pueden escribir, y escribir un bit cuyo significado no se conoce produce un estado que el juego quizá no sepa leer, sin ningún aviso.
La sección las marca «sin derivar», y esa etiqueta es toda la protección que hay. Es consistente con el resto del panel (es una herramienta de QA, no una interfaz a prueba de tontos), pero conviene saber que el ruido no está sólo en la lectura.
Sección 6 · Transportes (7 campos)

transportTile en byte crudo, más el estado de la fragata y el viento: casco, esquifes, dirección de vela, contador de deriva por viento, toggle del HMS Cape. Y un botón de QA para vaciar el pool de enemigos.
Verificación existente: docs/verdicts/debug-save-editor/01-transportes.png.
Sección 7 · NPCs (dead / met) — 65 campos
La sección más grande: por (localización, índice de NPC), un desplegable de localización y 32 + 32 casillas (muerto / conocido).
⚠️ Ojo: el índice no es la posición en un array — es un índice propio del NPC. Confundirlos hace que se marque al vecino de al lado.
Verificación existente: docs/verdicts/debug-save-editor/03-npcs-dead.png.
Sección 8 · Mazmorra · salas despejadas (17 campos)

El bitmap de salas ya despejadas: 7 ranuras × 16 salas.
Y una rareza del binario que el panel refleja en vez de disimular: Deceit y Despise colapsan en la ranura 0. Las ocho mazmorras comparten siete ranuras. La etiqueta lo dice —«Deceit / Despise»— y la derivación vive en dungeonClearedBitIndex.
Un editor que hubiera «arreglado» eso dando ocho ranuras habría escrito bits que el juego no lee.
Verificación existente: docs/verdicts/debug-save-editor/02-mazmorra-salas.png.
Sección 9 · Moonstones (32 campos)

Posición y estado de las ocho piedras lunares.
Secciones 10-13 · Inventario (120 campos)
| Sección | Campos | Captura |
|---|---|---|
| Reactivos | 8 | ![]() |
| Equipo (stock) | 48 | ![]() |
| Hechizos | 48 | ![]() |
| Pociones / Pergaminos | 16 | ![]() |
Cada celda es un campo numérico etiquetado con el nombre real del ítem, leído de InventoryDetails.json. No hay índices desnudos que obliguen a consultar una tabla aparte.
La guarda que impide que esto se quede atrás
El editor de save mantiene un manifiesto de cobertura —COVERED_STATE_KEYS y EXCLUDED_STATE_KEYS— que es la fuente de un test de completitud: si alguien añade un campo nuevo al GameState y no lo pone en ninguna de las dos listas, el guard se pone rojo.
Es la diferencia entre un panel de depuración que envejece bien y uno que a los seis meses edita el 70% del estado sin que nadie sepa cuál 70%. La decisión que se fuerza no es «añade el campo al panel», sino la más barata y más útil: decide y declara si viaja o si está excluido.
Lo que este panel no es
- No es un editor de partidas para jugadores. Es QA. Está detrás de una tecla que no se anuncia en producción.
- No sustituye al panel de guardado (F5, 3 · Experiencia §5): ese es de cara al jugador, exporta e importa, y no edita nada.
- No es cero-rand del todo: «Entrar mazmorra (flujo real)» sí puede consumir el stream, y lo lleva escrito en rojo.







