---
name: rmxp-agent
description: >
  Agente para modificar proyectos de Pokémon Essentials v21.1 en RPG Maker XP.
  Actívate cuando el usuario hable de mapas, eventos, NPCs, Pokémon, objetos,
  movimientos, plugins, warps, tiendas, diálogos o cualquier elemento del fangame.
---

# RMXP Agent — Pokémon Essentials v21.1

## Tu ventaja como IDE

Tienes acceso nativo al filesystem completo del proyecto con 1M de contexto.
**Úsalo.** No necesitas tools MCP para leer archivos de texto.

```
LEE DIRECTAMENTE (sin MCP):        USA MCP tools (binario Marshal):
  PBS/pokemon.txt                    list_maps / get_map_info
  PBS/moves.txt                      get_map_section
  PBS/items.txt                      add_npc / add_warp / add_mart
  PBS/trainers.txt                   add_item_ball / add_script_event
  Plugins/**/*.rb                    write_pokemon / write_move / write_item
  Scripts.rxdata (si es legible)     create_map / delete_event / undo
```

---

## Flujo estándar para cualquier tarea

```
1. LEE el contexto relevante directamente del filesystem
   → PBS/*.txt para datos de especies, items, movimientos
   → Plugins/*.rb para confirmar métodos Ruby disponibles

2. RAZONA sobre qué necesitas cambiar y cómo

3. USA las MCP tools solo para escribir o para leer .rxdata

4. CONFIRMA el resultado al usuario

**Setup de RxDataTools (eventos/mapas desde Python):**
```python
import sys
sys.path.insert(0, r'd:\Github\Test-Cuantico\bridge')  # bridge/rxdata_bridge.py
sys.path.insert(0, r'd:\Github\AgenteRuby\tools')      # tools/rxdata_tools.py
from rxdata_tools import RxDataTools

RUBY_PATH = r'C:\Ruby33-x64\bin\ruby.exe'  # Ruby x64, NO el arm
tools = RxDataTools(r'd:\Github\Test-Cuantico', ruby_path=RUBY_PATH)
```

**Operaciones comunes:**
```python
tools.get_map_info(map_id)                  # IDs, coords y nombres de eventos existentes
tools.find_free_positions(map_id, n)        # N posiciones libres (sin eventos)
tools.add_script_event(map_id, nombre, x, y, ruby_code, trigger=0, graphic="BW (15)")  
tools.add_npc(map_id, nombre, x, y, graphic, ["línea1", "línea2"])
tools.delete_event(map_id, event_id)        # Necesario antes de recrear si el script tiene error
tools.undo()                                # Revierte la última escritura (backup automático)
```

> ⚠️ `edit_event_page(map_id, eid, page, {'list': string})` **NO funciona** — el bridge espera lista de dicts, no un string. Si hay que corregir el script de un evento ya creado: usa `delete_event` + `add_script_event` con el código correcto.
```

---

## Flujos específicos

### Crear un NPC
```
1. Lee PBS/pokemon.txt y Plugins/ si el NPC interactúa con datos del juego
2. get_map_info(map_id)        → estado del mapa y eventos existentes
3. find_free_positions(map_id) → coordenadas disponibles
4. Si el usuario te proporciona una IMAGEN del mapa: úsala atentamente para estimar coordenadas lógicas visuales para ubicar eventos (al lado del agua, junto a una tienda de campaña) en lugar de usar posiciones libres completamente aleatorias/ciegas.
5. add_npc(...)                → crear el evento
```

### Crear un evento con script Ruby
```
REGLA DE ORO: Para cualquier evento o NPC que involucre lógica más allá de un simple diálogo de texto, DEBES generar una función en Ruby en la carpeta Plugins (por ejemplo, crear un archivo `Agent_Functions.rb` dentro de una carpeta nueva como `Plugins/Agente/`) y luego llamar a esa función desde el evento usando un comando de tipo "Script". Esto evita errores al generar datos crudos en JSON y hace el trabajo mucho más cómodo.

¡MUY IMPORTANTE!: Si creas una nueva carpeta dentro de `Plugins/`, Pokémon Essentials REQUIERE que exista un archivo `meta.txt` dentro de ella (con algo básico como `Name = MiPlugin` y `Version = 1.0`). Si omites este archivo, el juego no compilará tu script y arrojará un error `NameError: uninitialized constant`.

1. Escribe la lógica estructurada en un archivo nuevo o existente dentro de Plugins/NuevaCarpeta/.
2. Asegúrate de incluir un archivo `meta.txt` en la misma carpeta.
3. Lee Plugins/**/*.rb buscando métodos que necesites.
4. Para eventos de 1 sola acción: 
   add_script_event(..., ruby_code="NombreDeTuModulo.tu_funcion_genial()")
5. Para eventos multifase (ej. Entrenador que interactúa, pelea y luego dice otra cosa):
   add_multipage_event(..., pages_config=[
      {"ruby_code": "Modulo.fase1()", "graphic": "BW (10)"},
      {"ruby_code": "Modulo.fase2()", "graphic": "BW (10)", "condition": {"self_switch": "A"}}
   ])
```

### Modificar una especie Pokémon
```
1. Lee PBS/pokemon.txt directamente → encuentra la especie y su estado actual
2. Razona qué campos cambiar
3. write_pokemon(species, {campos_a_cambiar})  → validación automática
```

### Añadir tienda
```
1. Lee PBS/items.txt → confirma nombres internos de los objetos
2. get_map_info(map_id) → contexto del mapa
3. add_mart(map_id, ..., items=["POTION", "POKEBALL"])
```

### Warp entre mapas
```
1. list_maps()            → IDs de origen y destino
2. get_map_info(dest_id)  → coordenadas válidas de llegada
3. add_warp(...)
```

---

## Reglas que nunca se saltan

1. **Leer antes de escribir.** Siempre lee el estado actual antes de modificar.

2. **No inventar métodos Ruby.** Busca en Plugins/ antes de usarlos en scripts.
   Métodos confirmados en PE v21.1:
   `pbAddPokemon`, `pbGiveMoney`, `pbTakeMoney`, `pbMessage`, `pbHealPlayer`,
   `pbTrainerBattle`, `pbSetSelfSwitch`, `pbItemBall`, `pbPokemonMart`,
   `pbReceiveItem`, `pbConfirmMessage`

   **API del Bag (objeto `$bag`)** — estos son los únicos métodos válidos:
   | Método | Uso |
   |---|---|
   | `$bag.has?(:ITEM)` | Comprueba si el jugador tiene el objeto |
   | `$bag.has?(:ITEM, cantidad)` | Comprueba si tiene al menos N unidades |
   | `$bag.add(:ITEM, cantidad)` | Añade objetos al bag |
   | `$bag.remove(:ITEM, cantidad)` | Elimina objetos del bag |
   
   > ⛔ **NUNCA uses** `$PokemonBag.pbHasItem?` ni `$PokemonBag.pbDeleteItem` — esas APIs son de versiones antiguas de PE y causan `NoMethodError for nil:NilClass`.

3. **Ante cualquier error de rxdata** → ofrece `undo` inmediatamente.

4. **BaseStats** siempre 6 números separados por comas: `45,49,49,45,65,65`

5. **Tipos** siempre en mayúsculas: `FIRE`, `WATER`, `PSYCHIC`...
6. **EVs** deben usar nombres largos: `SPECIAL_ATTACK` y `SPECIAL_DEFENSE` (nunca SP_ATK o similares).
7. **Evolución por felicidad**: Usar `Happiness,X` (donde X suele ser 160 o 220).
8. **Evoluciones múltiples**: Siempre separar por comas en una sola línea o usar pokemon_forms si aplica.
9. **Codificación PBS**: Los archivos PBS DEBEN guardarse siempre en **UTF-8 con BOM** (`utf-8-sig` en Python). Esto evita el error `invalid byte sequence` en el compilador de Essentials.
10. **Caracteres especiales**: Evita usar herramientas que conviertan automáticamente caracteres (como PowerShell >) sin especificar el encoding UTF-8 explícitamente.
11. **pokemon_metrics.txt**: NO edites manualmente los valores de este archivo (BackSprite, FrontSprite, ShadowSprite). El juego/servidor los calcula automáticamente. Solo añade la sección `[ESPECIE]` si es un Pokémon nuevo para evitar errores de compilación, pero deja que el programa asigne los valores.
12. **BOM duplicado**: Al leer con `utf-8-sig` y guardar con `utf-8-sig`, Python añade un segundo BOM. Causas: usar `open(..., encoding='utf-8-sig')` para leer Y para escribir. **Fix**: leer con `utf-8-sig` (strip automático) y escribir con `'wb'` + `BOM + content.encode('utf-8')`, o bien con `encoding='utf-8'` (sin sig) y escribir manualmente `b'\xef\xbb\xbf'` al inicio.
13. **Linefeed en campos de lista PBS** (`Sintaxis de línea incorrecta, se esperaba XXX=YYY`): Ocurre cuando un campo con valores separados por comas (Flags, Moves, MapMetadata, etc.) se parte en varias líneas. El compiler espera la línea completa en una sola línea. **Fix estándar** con Python:
    ```python
    import re
    with open(path, 'rb') as f: raw = f.read()
    BOM = b'\xef\xbb\xbf'
    bom = raw[:3] == BOM
    content = raw[3:].decode('utf-8') if bom else raw.decode('utf-8')
    fixed = re.sub(r'[\r\n]+\s*,', ',', content)  # Une líneas rotas por coma
    with open(path, 'wb') as f: f.write((BOM if bom else b'') + fixed.encode('utf-8'))
    ```
    **NUNCA** uses `newline=''` en `open()` al escribir PBS — usa modo binario `'wb'` para control total. **NO edites pokemon_metrics.txt**: sus campos los asigna el juego automáticamente.

---

## Referencia rápida RGSS

| Trigger | Significado |
|---|---|
| 0 | Acción (botón A frente al evento) |
| 1 | Tocar jugador |
| 2 | Tocar evento |
| 3 | Automático al entrar al mapa |
| 4 | Proceso paralelo |


## Pluguins y Variables Clave (Proyecto Cuántico)

El proyecto contiene múltiples plugins personalizados con mecánicas complejas que puedes usar en eventos:

### 1. Sistema de Torres / Roguelike (`Towers`)
*   **Reinicio**: `Towers.InicioTorre` (ResetStats, Piso actual `$game_variables[83] = 1`, Vidas `$game_variables[132] = 3`)
*   **Viaje**: `Towers.teletransporteInteligente(bioma)`
*   **Bendiciones** (Variables 120-131). Usa `Bendiciones.darBendicion` o `Bendiciones.darPlegariaDivina`.

### 2. Level Caps EX
*   Variable de Límite de Nivel: `1300`
*   Modalidad del Cap: Variable `27` (0 = Hard, 1 = Soft, 2 = Obedience).

### 3. Vínculos Sociales (Social Links)
*   Usa `$player.social_links.pbGainLinkBond(:BRUNO, 1)` (donde `:BRUNO` es el ID del NPC en la configuración) para subir nivel de vínculo. Da recompensas de EXP y Shiny passivas.
*   Otros IDs: `:MARINA`, `:LUNA`, `:CAPITAN_VIENTO`, `:DOCTORA_SAGE`, `:CHEF_FUEGO`, `:SOFIA`.

### 4. Efectos, Condiciones y Ludopatía
*   `Utilidades.lose_hp(percent)` / `Utilidades.heal_vida(percent)`: Para dañar o curar al equipo entero por porcentaje.
*   `Utilidades.faint_random_pokemon`: Debilita un Pokémon aleatorio.
*   **⚠️ REGLA DE ORO DE LUDOPATÍA Y MONEDAS**: El sistema de apuestas (`SistemaApuestas.apostar`) y el uso de PokéMonedas (Variable 86) **SOLAMENTE** deben utilizarse cuando el usuario lo pida expresamente, o cuando indique que se está creando contenido para una **"Torre"**. No incluyas apuestas o mercaderes raros por defecto en NPCs normales de rutas/ciudades.
*   PP Tienda Pokeballs: `Utilidades.compraPokeballs`. Usa Puntos Pokémon (Variable 91).

### 5. Minijuegos
*   Juego Snake: Ejecuta `pbEkansGame` en un evento. Para usar el highscore: `Ekans_Game.max_score`. (No lo incluyas sin pedirlo).

### 6. Batallas Aleatorias Genéricas
*   `BattleManager.iniciar_batalla_aleatoria`: Invoca un encuentro con NPC aleatorio del script `Entrenadores_Random/Script.rb` sin crear evento. (Útil para emboscadas, pero no abusar en NPCs pacíficos).


| Dirección | Valor |
|---|---|
| Abajo (hacia jugador) | 2 |
| Izquierda | 4 |
| Derecha | 6 |
| Arriba | 8 |

---

## Generación de Gráficos (FakeMons, Megaevoluciones)

1. **Fondo del sprite — Decidir ANTES de escribir el prompt:**

   > ⚠️ Elegir el fondo equivocado destruye el sprite al hacer chroma key. Analiza el color predominante del Pokémon **primero** y elige el fondo opuesto.

   | Color predominante del Pokémon | Fondo del prompt | Comando de limpieza |
   |---|---|---|
   | Cualquier color (caso general) | `SOLID MAGENTA background (#FF00FF)` | `python clean_sprite2.py src.png dst.png 160 1 255 0 255` |
   | Rosa, magenta, lila, fucsia | `SOLID GREEN background (#00FF00)` | `python clean_sprite2.py src.png dst.png 160 1 0 255 0` |
   | Verde, lima, amarillo-verdoso | `SOLID BLUE background (#0000FF)` | `python clean_sprite2.py src.png dst.png 160 1 0 0 255` |

   **Regla de duda**: si el Pokémon tiene partes rosas aunque sean pequeñas pero prominentes (como Luvdisc o Cupidisc), usa fondo VERDE.

2. **Prompts Clave — Reglas de Oro Estéticas:**
   - **Paleta**: `Limited palette of max 5 colors per body part`.
   - **Sombreado**: `Hard cel-shading only, NO gradients, NO soft shadows`. 3 tonos: Base, Sombra Dura, Highlight.
   - **Delineado**: `Thick black outer outline, darker-shade internal outlines`.
   - **Texturas**: `Zero textures, NO feathers, NO scales, NO realistic skin`. Bloques de color puros.
   - **Ejemplo**: `Official GBA style battle sprite of [X]. Flat simple colors, 5-color palette, cel-shading, thick black outlines, [FONDO ELEGIDO].`

3. **Pipeline de Producción Completo:**
   ```
   1. Decide el fondo (tabla anterior) ANTES de generar
   2. IA genera sprite con el fondo elegido y límites de color
   3. python clean_sprite2.py <origen.png> <destino.png> 160 1 [R G B del fondo]
      ├── Chroma Key Semántico: borra el color de fondo ± tolerancia
      ├── Erosión de bordes x6 pasadas: elimina anti-aliasing residual
      ├── Defringe: borra halos blancos
      └── NEAREST resize (160x160) → bordes duros de píxel, escala correcta para el juego
   ```

## Herramientas del Agente (Mantenimiento)

| Script | Propósito | Estado |
|---|---|---|
| `process_new_pokemon.py` | Pipeline avanzado completo: Chroma Key semántico, erosión de bordes, defringe, resize. | ✅ Activo |
| `clean_sprite2.py` | Alias de `process_new_pokemon.py`. Tool principal para sprites de IA con anti-aliasing. Mismo pipeline. | ✅ Activo |
| `clean_sprite.py` | Pipeline básico: Chroma Key euclidiano simple + defringe. Más rápido, para sprites ya limpios. | ✅ Activo |

> ⚠️ **No crear scripts de reparación PBS one-shot** (`fix_encoding.py`, `repair_pbs_all.py`, etc.). En su lugar, usar el snippet de Python inline de la regla 13 directamente en la terminal, o hacer `git checkout <commit> -- PBS/<archivo>.txt` para revertir al estado limpio del repositorio.

## Diagnóstico rápido de errores PBS

| Error del compilador | Causa probable | Acción |
|---|---|---|
| `invalid byte sequence in UTF-8` | Archivo no es UTF-8 o BOM duplicado | Verificar bytes iniciales con Python `repr(open(f,'rb').read(10))`. Si hay doble BOM (`efbbbfefbbbf`), aplicar regla 12. |
| `Se esperaba una sección al principio` | BOM duplicado o caracteres antes del primer `[` | Aplicar regla 12. |
| `Sintaxis de línea incorrecta (XXX=YYY)` | Campo de lista partido en varias líneas (linefeed en medio de Flags, Moves, etc.) | Aplicar regla 13 sobre el archivo indicado en el error. |
| `Valor X no definido en GameData::Y` | Nombre de campo con acento o carácter especial (ej: `BipedíalTail`) | El archivo fue guardado con encoding incorrecto o tiene caracteres corruptos. Revisar con `repr()`. |
| `NameError: uninitialized constant` | Plugin nuevo sin `meta.txt` en su carpeta | Crear `meta.txt` en `Plugins/NuevaCarpeta/` con `Name = X` y `Version = 1.0`. |
