- afegides claus i portes al editor
- fix: crear una nova habitació no modificava la memoria, soles els fitxers
This commit is contained in:
18
CLAUDE.md
18
CLAUDE.md
@@ -214,14 +214,18 @@ When refactoring code (especially with `/refactor-class` command):
|
|||||||
- **Nunca** escribas yaml a pelo con `std::ofstream` desde otro sitio (especialmente desde `MapEditor::createNewRoom`, que ya delega en `RoomFormat::createDefault` + `RoomFormat::saveYAML`). El editor solo manipula `Room::Data`; el formato yaml es invisible para él. Saltarse esta regla causa bugs como el del crash de `06.yaml` (formato hardcoded en `createNewRoom` que se desincroniza del parser real).
|
- **Nunca** escribas yaml a pelo con `std::ofstream` desde otro sitio (especialmente desde `MapEditor::createNewRoom`, que ya delega en `RoomFormat::createDefault` + `RoomFormat::saveYAML`). El editor solo manipula `Room::Data`; el formato yaml es invisible para él. Saltarse esta regla causa bugs como el del crash de `06.yaml` (formato hardcoded en `createNewRoom` que se desincroniza del parser real).
|
||||||
|
|
||||||
6. **Nuevas entidades de room — soporte completo en editor y consola:** Cuando añadas un tipo nuevo de entidad a las rooms (al estilo de `enemies`, `items`, `platforms`, `keys`, `doors`), no basta con el parser/serializer y el manager runtime. Debes implementar también su contrapartida en el editor y en la consola, en paridad con los tipos existentes:
|
6. **Nuevas entidades de room — soporte completo en editor y consola:** Cuando añadas un tipo nuevo de entidad a las rooms (al estilo de `enemies`, `items`, `platforms`, `keys`, `doors`), no basta con el parser/serializer y el manager runtime. Debes implementar también su contrapartida en el editor y en la consola, en paridad con los tipos existentes:
|
||||||
- **Selección visual** en `MapEditor` (click sobre la entidad → `selection_` con su tipo y índice).
|
- **Renderizado dentro del editor**: añadir la llamada al `room_->renderXxx()` correspondiente en `MapEditor::render()`. Sin esto la entidad no se ve cuando el editor está activo (aunque sí se vea en juego), porque el editor renderiza entidades por separado del flujo normal del juego.
|
||||||
- **Drag & drop** para mover la entidad por el grid (con autosave al soltar).
|
- **Selección visual** en `MapEditor` (click sobre la entidad → `selection_` con su tipo y índice). Extender el loop de hit test en `MapEditor::handleMouseDown` con el nuevo `EntityType`.
|
||||||
- **Entry en la statusbar** del editor cuando la entidad está seleccionada (mostrar id, animación, etc).
|
- **Drag & drop** para mover la entidad por el grid (con autosave al soltar). Extender `moveEntityVisual` y `commitEntityDrag`.
|
||||||
- **Comandos de consola**: `add <tipo>`, `delete`, `set <propiedad> <valor>` para esa entidad (ver el bloque `setEnemyProperty` / `setItemProperty` / `setPlatformProperty` en `map_editor.cpp` y `cmdSet` en `console_commands.cpp`).
|
- **Entry en la statusbar** del editor cuando la entidad está seleccionada (mostrar id, animación, etc). Switch en `updateStatusBarInfo`.
|
||||||
- **Renderizado de hitbox/boundary** en `renderEntityBoundaries` cuando esté seleccionada o cuando el jugador active el overlay de debug.
|
- **Comandos de consola**: `cmd<Tipo>` para `add/delete/duplicate` y routing en `cmdSet` para `setXxxProperty`. Ver `cmdEnemy`/`cmdItem`/`cmdPlatform`/`cmdKey`/`cmdDoor` en `console_commands.cpp`. Registrar en `data/console/commands.yaml` también.
|
||||||
- **EntityType enum** en `map_editor.hpp` extendido con el nuevo tipo.
|
- **Helpers genéricos**: `entityCount`, `entityRect`, `entityPosition`, `entityDataCount`, `entityLabel` deben cubrir el nuevo tipo (switch sobre `EntityType`).
|
||||||
|
- **Manager debug API**: el manager de la entidad necesita `getCount()` y `getXxx(int)` bajo `#ifdef _DEBUG` para que el editor pueda iterar.
|
||||||
|
- **Setter de posición**: la entidad necesita `setPosition(float, float)` bajo `#ifdef _DEBUG` para el drag visual.
|
||||||
|
- **`EntityType enum`** en `map_editor.hpp` extendido con el nuevo tipo.
|
||||||
|
- **Si la entidad afecta a colisiones** (como `Door` que escribe `WALL` en el `CollisionMap`), el manager debe exponer `move<Tipo>(idx, x, y)` y `remove<Tipo>(idx)` que encapsulen el bookkeeping. **El editor no debe tocar nunca el `CollisionMap` directamente.**
|
||||||
|
|
||||||
Pendiente histórico: las **llaves (`Key`) y puertas (`Door`)** se añadieron al sistema de rooms en su día sin esta contrapartida en el editor. Hay que migrarlas siguiendo este patrón. Cualquier entidad nueva debe nacer ya con soporte completo, no diferirlo "para después".
|
Cualquier entidad nueva debe nacer con soporte completo, no diferirlo "para después".
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ assets:
|
|||||||
- ${PREFIX}/data/room/03.yaml
|
- ${PREFIX}/data/room/03.yaml
|
||||||
- ${PREFIX}/data/room/04.yaml
|
- ${PREFIX}/data/room/04.yaml
|
||||||
- ${PREFIX}/data/room/05.yaml
|
- ${PREFIX}/data/room/05.yaml
|
||||||
|
- ${PREFIX}/data/room/06.yaml
|
||||||
|
|
||||||
# TILESETS
|
# TILESETS
|
||||||
tilesets:
|
tilesets:
|
||||||
|
|||||||
@@ -251,9 +251,23 @@ categories:
|
|||||||
completions:
|
completions:
|
||||||
PLATFORM: [ADD, DELETE, DUPLICATE]
|
PLATFORM: [ADD, DELETE, DUPLICATE]
|
||||||
|
|
||||||
|
- keyword: KEY
|
||||||
|
handler: cmd_key
|
||||||
|
description: "Add, delete or duplicate key"
|
||||||
|
usage: "KEY <ADD|DELETE|DUPLICATE>"
|
||||||
|
completions:
|
||||||
|
KEY: [ADD, DELETE, DUPLICATE]
|
||||||
|
|
||||||
|
- keyword: DOOR
|
||||||
|
handler: cmd_door
|
||||||
|
description: "Add, delete or duplicate door"
|
||||||
|
usage: "DOOR <ADD|DELETE|DUPLICATE>"
|
||||||
|
completions:
|
||||||
|
DOOR: [ADD, DELETE, DUPLICATE]
|
||||||
|
|
||||||
- keyword: SET
|
- keyword: SET
|
||||||
handler: cmd_set
|
handler: cmd_set
|
||||||
description: "Set property (enemy, item, platform or room)"
|
description: "Set property (enemy, item, platform, key, door or room)"
|
||||||
usage: "SET <property> <value>"
|
usage: "SET <property> <value>"
|
||||||
dynamic_completions: true
|
dynamic_completions: true
|
||||||
completions:
|
completions:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ room:
|
|||||||
connections:
|
connections:
|
||||||
up: null
|
up: null
|
||||||
down: null
|
down: null
|
||||||
left: null
|
left: 06.yaml
|
||||||
right: 02.yaml
|
right: 02.yaml
|
||||||
|
|
||||||
# Colores de los objetos
|
# Colores de los objetos
|
||||||
@@ -30,7 +30,7 @@ tilemap:
|
|||||||
- [33, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, 169, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
- [33, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, 169, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 169, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 169, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 169, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 169, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
- [7, 7, 7, 7, 7, 7, 7, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
- [33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
@@ -53,7 +53,7 @@ tilemap:
|
|||||||
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
@@ -63,3 +63,15 @@ tilemap:
|
|||||||
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 3, 0, 0, 0, 0, 0, 0]
|
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 3, 0, 0, 0, 0, 0, 0]
|
||||||
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 3, 0, 0, 0, 0, 0]
|
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 3, 0, 0, 0, 0, 0]
|
||||||
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
|
# Llaves en esta habitación
|
||||||
|
keys:
|
||||||
|
- animation: key1.yaml
|
||||||
|
id: "1"
|
||||||
|
position: {x: 23, y: 11}
|
||||||
|
|
||||||
|
# Puertas en esta habitación
|
||||||
|
doors:
|
||||||
|
- animation: door1.yaml
|
||||||
|
id: "1"
|
||||||
|
position: {x: 16, y: 11}
|
||||||
|
|
||||||
|
|||||||
65
data/room/06.yaml
Normal file
65
data/room/06.yaml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
room:
|
||||||
|
zone: cave
|
||||||
|
|
||||||
|
# Conexiones de la habitación (null = sin conexión)
|
||||||
|
connections:
|
||||||
|
up: null
|
||||||
|
down: null
|
||||||
|
left: null
|
||||||
|
right: 01.yaml
|
||||||
|
|
||||||
|
# Colores de los objetos
|
||||||
|
itemColor1: 11
|
||||||
|
itemColor2: 12
|
||||||
|
|
||||||
|
# Dirección de la cinta transportadora: left, none, right
|
||||||
|
conveyorBelt: none
|
||||||
|
|
||||||
|
# Tilemap: 21 filas x 32 columnas @ 8px/tile
|
||||||
|
tilemap:
|
||||||
|
# Mapa de dibujo (indices de tiles, -1 = vacio)
|
||||||
|
draw:
|
||||||
|
- [25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25]
|
||||||
|
- [25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25]
|
||||||
|
- [25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25]
|
||||||
|
- [49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 513, 25]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 513, 25, 25]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 513, 25, 25, 25]
|
||||||
|
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 513, 25, 25, 25, 25]
|
||||||
|
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 25]
|
||||||
|
- [25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25]
|
||||||
|
# Mapa de colisiones (0 = vacio, 1 = solido)
|
||||||
|
collision:
|
||||||
|
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
|
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
|
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
|
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, 1]
|
||||||
|
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, 1, 1]
|
||||||
|
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
|
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
@@ -23,8 +23,10 @@
|
|||||||
#include "game/gameplay/room_format.hpp" // Para RoomFormat
|
#include "game/gameplay/room_format.hpp" // Para RoomFormat
|
||||||
#include "game/entities/player.hpp" // Para Player
|
#include "game/entities/player.hpp" // Para Player
|
||||||
#include "game/game_control.hpp" // Para GameControl
|
#include "game/game_control.hpp" // Para GameControl
|
||||||
|
#include "game/gameplay/door_manager.hpp" // Para DoorManager
|
||||||
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
|
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
|
||||||
#include "game/gameplay/item_manager.hpp" // Para ItemManager
|
#include "game/gameplay/item_manager.hpp" // Para ItemManager
|
||||||
|
#include "game/gameplay/key_manager.hpp" // Para KeyManager
|
||||||
#include "game/gameplay/platform_manager.hpp" // Para PlatformManager
|
#include "game/gameplay/platform_manager.hpp" // Para PlatformManager
|
||||||
#include "game/gameplay/room.hpp" // Para Room
|
#include "game/gameplay/room.hpp" // Para Room
|
||||||
#include "game/gameplay/zone.hpp" // Para Zone::Data
|
#include "game/gameplay/zone.hpp" // Para Zone::Data
|
||||||
@@ -328,7 +330,17 @@ void MapEditor::autosave() {
|
|||||||
room_data_.items[i].y = pos.y;
|
room_data_.items[i].y = pos.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sincronizar posiciones de llaves desde los sprites vivos a room_data_
|
||||||
|
// (mismo patrón que items: el sprite es la fuente de verdad durante el drag)
|
||||||
|
auto* key_mgr = room_->getKeyManager();
|
||||||
|
for (int i = 0; i < key_mgr->getCount() && i < static_cast<int>(room_data_.keys.size()); ++i) {
|
||||||
|
SDL_FPoint pos = key_mgr->getKey(i)->getPos();
|
||||||
|
room_data_.keys[i].x = pos.x;
|
||||||
|
room_data_.keys[i].y = pos.y;
|
||||||
|
}
|
||||||
|
|
||||||
// Platforms are already synced via resetToInitialPosition during drag commit
|
// Platforms are already synced via resetToInitialPosition during drag commit
|
||||||
|
// Doors are already synced via DoorManager::moveDoor in commitEntityDrag
|
||||||
|
|
||||||
RoomFormat::saveYAML(file_path_, room_data_);
|
RoomFormat::saveYAML(file_path_, room_data_);
|
||||||
}
|
}
|
||||||
@@ -413,10 +425,13 @@ void MapEditor::render() {
|
|||||||
// Renderizar los marcadores de boundaries y líneas de ruta (debajo de los sprites)
|
// Renderizar los marcadores de boundaries y líneas de ruta (debajo de los sprites)
|
||||||
renderEntityBoundaries();
|
renderEntityBoundaries();
|
||||||
|
|
||||||
// Renderizar entidades normales: enemigos (animados en posición inicial), items, plataformas, jugador
|
// Renderizar entidades normales: enemigos (animados en posición inicial), items,
|
||||||
|
// plataformas, llaves, puertas, jugador
|
||||||
room_->renderEnemies();
|
room_->renderEnemies();
|
||||||
room_->renderItems();
|
room_->renderItems();
|
||||||
room_->renderPlatforms();
|
room_->renderPlatforms();
|
||||||
|
room_->renderKeys();
|
||||||
|
room_->renderDoors();
|
||||||
player_->render();
|
player_->render();
|
||||||
|
|
||||||
// Renderizar highlight de selección (encima de los sprites)
|
// Renderizar highlight de selección (encima de los sprites)
|
||||||
@@ -608,8 +623,10 @@ void MapEditor::handleMouseDown(float game_x, float game_y) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Hit test on entity initials
|
// 2. Hit test on entity initials. DOOR antes que KEY/ITEM porque ocupa más
|
||||||
for (auto type : {EntityType::ENEMY, EntityType::PLATFORM, EntityType::ITEM}) {
|
// espacio (1×4 tiles); ENEMY/PLATFORM van primero porque pueden estar
|
||||||
|
// dibujados encima de items pequeños.
|
||||||
|
for (auto type : {EntityType::ENEMY, EntityType::PLATFORM, EntityType::DOOR, EntityType::KEY, EntityType::ITEM}) {
|
||||||
for (int i = 0; i < entityCount(type); ++i) {
|
for (int i = 0; i < entityCount(type); ++i) {
|
||||||
SDL_FRect rect = entityRect(type, i);
|
SDL_FRect rect = entityRect(type, i);
|
||||||
if (pointInRect(game_x, game_y, rect)) {
|
if (pointInRect(game_x, game_y, rect)) {
|
||||||
@@ -724,6 +741,33 @@ auto MapEditor::commitEntityDrag() -> bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case EntityType::KEY:
|
||||||
|
if (IDX >= 0 && IDX < static_cast<int>(room_data_.keys.size())) {
|
||||||
|
// El sprite ya está en su posición visual final desde moveEntityVisual.
|
||||||
|
// Solo hay que actualizar room_data_; el autosave hará el sync inverso
|
||||||
|
// sprite→data igual que con items.
|
||||||
|
room_data_.keys[IDX].x = drag_.snap_x;
|
||||||
|
room_data_.keys[IDX].y = drag_.snap_y;
|
||||||
|
selection_ = {EntityType::KEY, IDX};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EntityType::DOOR:
|
||||||
|
if (IDX >= 0 && IDX < static_cast<int>(room_data_.doors.size())) {
|
||||||
|
// Truco crítico: durante el drag, moveEntityVisual movió el sprite
|
||||||
|
// pero los WALLs del CollisionMap siguen en la posición antigua. Antes
|
||||||
|
// de llamar a moveDoor (que limpia los tiles "actuales" y escribe los
|
||||||
|
// nuevos), restauramos el sprite a su posición vieja para que coincida
|
||||||
|
// con los tiles. moveDoor luego hace el ciclo limpio y completo.
|
||||||
|
auto* door_mgr = room_->getDoorManager();
|
||||||
|
door_mgr->getDoor(IDX)->setPosition(room_data_.doors[IDX].x, room_data_.doors[IDX].y);
|
||||||
|
room_data_.doors[IDX].x = drag_.snap_x;
|
||||||
|
room_data_.doors[IDX].y = drag_.snap_y;
|
||||||
|
door_mgr->moveDoor(IDX, drag_.snap_x, drag_.snap_y);
|
||||||
|
selection_ = {EntityType::DOOR, IDX};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -799,6 +843,20 @@ void MapEditor::moveEntityVisual() {
|
|||||||
room_->getPlatformManager()->getPlatform(drag_.index)->resetToInitialPosition(temp_data);
|
room_->getPlatformManager()->getPlatform(drag_.index)->resetToInitialPosition(temp_data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case EntityType::KEY:
|
||||||
|
if (drag_.index >= 0 && drag_.index < room_->getKeyManager()->getCount()) {
|
||||||
|
room_->getKeyManager()->getKey(drag_.index)->setPosition(drag_.snap_x, drag_.snap_y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EntityType::DOOR:
|
||||||
|
// Solo movemos el sprite visualmente. Los WALLs del CollisionMap NO
|
||||||
|
// se tocan durante el drag (la puerta arrastrada no debería bloquear
|
||||||
|
// su nueva posición todavía). El bookkeeping completo se hace en
|
||||||
|
// commitEntityDrag al soltar.
|
||||||
|
if (drag_.index >= 0 && drag_.index < room_->getDoorManager()->getCount()) {
|
||||||
|
room_->getDoorManager()->getDoor(drag_.index)->setPosition(drag_.snap_x, drag_.snap_y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -916,6 +974,10 @@ auto MapEditor::entityCount(EntityType type) const -> int {
|
|||||||
return room_->getItemManager()->getCount();
|
return room_->getItemManager()->getCount();
|
||||||
case EntityType::PLATFORM:
|
case EntityType::PLATFORM:
|
||||||
return room_->getPlatformManager()->getCount();
|
return room_->getPlatformManager()->getCount();
|
||||||
|
case EntityType::KEY:
|
||||||
|
return room_->getKeyManager()->getCount();
|
||||||
|
case EntityType::DOOR:
|
||||||
|
return room_->getDoorManager()->getCount();
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -929,6 +991,10 @@ auto MapEditor::entityRect(EntityType type, int index) -> SDL_FRect {
|
|||||||
return room_->getItemManager()->getItem(index)->getCollider();
|
return room_->getItemManager()->getItem(index)->getCollider();
|
||||||
case EntityType::PLATFORM:
|
case EntityType::PLATFORM:
|
||||||
return room_->getPlatformManager()->getPlatform(index)->getRect();
|
return room_->getPlatformManager()->getPlatform(index)->getRect();
|
||||||
|
case EntityType::KEY:
|
||||||
|
return room_->getKeyManager()->getKey(index)->getCollider();
|
||||||
|
case EntityType::DOOR:
|
||||||
|
return room_->getDoorManager()->getDoor(index)->getCollider();
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -959,6 +1025,10 @@ auto MapEditor::entityPosition(EntityType type, int index) const -> std::pair<fl
|
|||||||
const auto& path = room_data_.platforms[index].path;
|
const auto& path = room_data_.platforms[index].path;
|
||||||
return path.empty() ? std::pair{0.0F, 0.0F} : std::pair{path[0].x, path[0].y};
|
return path.empty() ? std::pair{0.0F, 0.0F} : std::pair{path[0].x, path[0].y};
|
||||||
}
|
}
|
||||||
|
case EntityType::KEY:
|
||||||
|
return {room_data_.keys[index].x, room_data_.keys[index].y};
|
||||||
|
case EntityType::DOOR:
|
||||||
|
return {room_data_.doors[index].x, room_data_.doors[index].y};
|
||||||
default:
|
default:
|
||||||
return {0.0F, 0.0F};
|
return {0.0F, 0.0F};
|
||||||
}
|
}
|
||||||
@@ -972,6 +1042,10 @@ auto MapEditor::entityDataCount(EntityType type) const -> int {
|
|||||||
return static_cast<int>(room_data_.items.size());
|
return static_cast<int>(room_data_.items.size());
|
||||||
case EntityType::PLATFORM:
|
case EntityType::PLATFORM:
|
||||||
return static_cast<int>(room_data_.platforms.size());
|
return static_cast<int>(room_data_.platforms.size());
|
||||||
|
case EntityType::KEY:
|
||||||
|
return static_cast<int>(room_data_.keys.size());
|
||||||
|
case EntityType::DOOR:
|
||||||
|
return static_cast<int>(room_data_.doors.size());
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -985,6 +1059,10 @@ auto MapEditor::entityLabel(EntityType type) -> const char* {
|
|||||||
return "item";
|
return "item";
|
||||||
case EntityType::PLATFORM:
|
case EntityType::PLATFORM:
|
||||||
return "platform";
|
return "platform";
|
||||||
|
case EntityType::KEY:
|
||||||
|
return "key";
|
||||||
|
case EntityType::DOOR:
|
||||||
|
return "door";
|
||||||
default:
|
default:
|
||||||
return "none";
|
return "none";
|
||||||
}
|
}
|
||||||
@@ -1203,6 +1281,28 @@ void MapEditor::updateStatusBarInfo() { // NOLINT(readability-function-cognitiv
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EntityType::KEY:
|
||||||
|
if (selection_.index < static_cast<int>(room_data_.keys.size())) {
|
||||||
|
const auto& k = room_data_.keys[selection_.index];
|
||||||
|
std::string anim = k.animation_path;
|
||||||
|
auto dot = anim.rfind('.');
|
||||||
|
if (dot != std::string::npos) { anim = anim.substr(0, dot); }
|
||||||
|
line2 = "key " + std::to_string(selection_.index) + ": " + anim;
|
||||||
|
line3 = "id: " + k.id;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EntityType::DOOR:
|
||||||
|
if (selection_.index < static_cast<int>(room_data_.doors.size())) {
|
||||||
|
const auto& d = room_data_.doors[selection_.index];
|
||||||
|
std::string anim = d.animation_path;
|
||||||
|
auto dot = anim.rfind('.');
|
||||||
|
if (dot != std::string::npos) { anim = anim.substr(0, dot); }
|
||||||
|
line2 = "door " + std::to_string(selection_.index) + ": " + anim;
|
||||||
|
line3 = "id: " + d.id;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case EntityType::NONE: {
|
case EntityType::NONE: {
|
||||||
// Propiedades de la habitación
|
// Propiedades de la habitación
|
||||||
std::string conv = "none";
|
std::string conv = "none";
|
||||||
@@ -1252,8 +1352,12 @@ auto MapEditor::getSetCompletions() const -> std::vector<std::string> {
|
|||||||
return {"TILE", "COUNTER"};
|
return {"TILE", "COUNTER"};
|
||||||
case EntityType::PLATFORM:
|
case EntityType::PLATFORM:
|
||||||
return {"ANIMATION", "SPEED", "LOOP", "EASING"};
|
return {"ANIMATION", "SPEED", "LOOP", "EASING"};
|
||||||
|
case EntityType::KEY:
|
||||||
|
return {"ID", "ANIMATION"};
|
||||||
|
case EntityType::DOOR:
|
||||||
|
return {"ID", "ANIMATION"};
|
||||||
default:
|
default:
|
||||||
return {"ITEMCOLOR1", "ITEMCOLOR2", "CONVEYOR", "TILESET", "UP", "DOWN", "LEFT", "RIGHT"};
|
return {"ZONE", "ITEMCOLOR1", "ITEMCOLOR2", "CONVEYOR", "TILESET", "MUSIC", "UP", "DOWN", "LEFT", "RIGHT"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1667,6 +1771,17 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string { //
|
|||||||
Room::Data new_room = RoomFormat::createDefault();
|
Room::Data new_room = RoomFormat::createDefault();
|
||||||
new_room.number = std::string(name_buf).substr(0, std::string(name_buf).find('.'));
|
new_room.number = std::string(name_buf).substr(0, std::string(name_buf).find('.'));
|
||||||
|
|
||||||
|
// Heredar la zona de la room actual (y sus tileset/música resueltos), para
|
||||||
|
// que el usuario no tenga que asignarla manualmente al expandir un nivel.
|
||||||
|
// No marcamos los overrides: si la room actual tenía overrides explícitos,
|
||||||
|
// la nueva se queda con los valores resueltos pero como heredados de zona.
|
||||||
|
new_room.zone = room_data_.zone;
|
||||||
|
const Zone::Data* zone = ZoneManager::get()->getZone(new_room.zone);
|
||||||
|
if (zone != nullptr) {
|
||||||
|
new_room.tile_set_file = zone->tile_set_file;
|
||||||
|
new_room.music = zone->music;
|
||||||
|
}
|
||||||
|
|
||||||
// Conexión recíproca: la nueva room conecta de vuelta a la actual
|
// Conexión recíproca: la nueva room conecta de vuelta a la actual
|
||||||
if (direction == "UP") {
|
if (direction == "UP") {
|
||||||
new_room.lower_room = room_path_;
|
new_room.lower_room = room_path_;
|
||||||
@@ -1687,15 +1802,39 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string { //
|
|||||||
Resource::Cache::get()->getRooms().emplace_back(
|
Resource::Cache::get()->getRooms().emplace_back(
|
||||||
RoomResource{.name = new_name, .room = std::make_shared<Room::Data>(new_room)});
|
RoomResource{.name = new_name, .room = std::make_shared<Room::Data>(new_room)});
|
||||||
|
|
||||||
// Conectar la room actual con la nueva (recíproco: ya hecho arriba para la nueva)
|
// Conectar la room actual con la nueva (recíproco: ya hecho arriba para la nueva).
|
||||||
|
// Actualizamos tres sitios para que la conexión sea visible inmediatamente:
|
||||||
|
// 1. room_data_ (la copia del editor) → será autosaveada al yaml
|
||||||
|
// 2. la Room viva del juego (room_) → para que el navigation funcione sin reload
|
||||||
|
// 3. la Room::Data cacheada en Resource::Cache → para que adjacent rooms y
|
||||||
|
// futuros reloads vean la conexión nueva
|
||||||
|
Room::Border border = Room::Border::NONE;
|
||||||
if (direction == "UP") {
|
if (direction == "UP") {
|
||||||
room_data_.upper_room = new_name;
|
room_data_.upper_room = new_name;
|
||||||
|
border = Room::Border::TOP;
|
||||||
} else if (direction == "DOWN") {
|
} else if (direction == "DOWN") {
|
||||||
room_data_.lower_room = new_name;
|
room_data_.lower_room = new_name;
|
||||||
|
border = Room::Border::BOTTOM;
|
||||||
} else if (direction == "LEFT") {
|
} else if (direction == "LEFT") {
|
||||||
room_data_.left_room = new_name;
|
room_data_.left_room = new_name;
|
||||||
|
border = Room::Border::LEFT;
|
||||||
} else if (direction == "RIGHT") {
|
} else if (direction == "RIGHT") {
|
||||||
room_data_.right_room = new_name;
|
room_data_.right_room = new_name;
|
||||||
|
border = Room::Border::RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (border != Room::Border::NONE) {
|
||||||
|
// Sincronizar con la Room viva (Game::room_cache_ apunta a este shared_ptr)
|
||||||
|
room_->setConnection(border, new_name);
|
||||||
|
|
||||||
|
// Sincronizar con Resource::Cache::rooms_ (datos crudos)
|
||||||
|
auto cached = Resource::Cache::get()->getRoom(room_path_);
|
||||||
|
if (cached) {
|
||||||
|
if (direction == "UP") { cached->upper_room = new_name; }
|
||||||
|
else if (direction == "DOWN") { cached->lower_room = new_name; }
|
||||||
|
else if (direction == "LEFT") { cached->left_room = new_name; }
|
||||||
|
else if (direction == "RIGHT") { cached->right_room = new_name; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!direction.empty()) { autosave(); }
|
if (!direction.empty()) { autosave(); }
|
||||||
@@ -2016,6 +2155,238 @@ auto MapEditor::duplicatePlatform() -> std::string {
|
|||||||
return "Duplicated as platform " + std::to_string(new_index);
|
return "Duplicated as platform " + std::to_string(new_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// LLAVES
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Modifica una propiedad de la llave seleccionada
|
||||||
|
auto MapEditor::setKeyProperty(const std::string& property, const std::string& value) -> std::string {
|
||||||
|
if (!active_) { return "Editor not active"; }
|
||||||
|
if (!hasSelectedKey()) { return "No key selected"; }
|
||||||
|
|
||||||
|
auto& key = room_data_.keys[selection_.index];
|
||||||
|
|
||||||
|
if (property == "ID") {
|
||||||
|
key.id = value;
|
||||||
|
// Recrear el Key (el id se pasa al constructor)
|
||||||
|
try {
|
||||||
|
room_->getKeyManager()->getKey(selection_.index) = std::make_shared<Key>(key);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
return std::string("Error: ") + e.what();
|
||||||
|
}
|
||||||
|
autosave();
|
||||||
|
return "id: " + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == "ANIMATION") {
|
||||||
|
std::string anim = toLower(value);
|
||||||
|
if (anim.find('.') == std::string::npos) { anim += ".yaml"; }
|
||||||
|
std::string old_anim = key.animation_path;
|
||||||
|
key.animation_path = anim;
|
||||||
|
try {
|
||||||
|
room_->getKeyManager()->getKey(selection_.index) = std::make_shared<Key>(key);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
key.animation_path = old_anim;
|
||||||
|
return std::string("Error: ") + e.what();
|
||||||
|
}
|
||||||
|
autosave();
|
||||||
|
return "animation: " + anim;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown property: " + property + " (use: id, animation)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea una nueva llave centrada en la habitación
|
||||||
|
auto MapEditor::addKey() -> std::string {
|
||||||
|
if (!active_) { return "Editor not active"; }
|
||||||
|
|
||||||
|
Key::Data new_key;
|
||||||
|
new_key.animation_path = "key1.yaml";
|
||||||
|
new_key.id = "1";
|
||||||
|
new_key.x = static_cast<float>(PlayArea::CENTER_X);
|
||||||
|
new_key.y = static_cast<float>(PlayArea::CENTER_Y);
|
||||||
|
|
||||||
|
room_data_.keys.push_back(new_key);
|
||||||
|
try {
|
||||||
|
room_->getKeyManager()->addKey(std::make_shared<Key>(new_key));
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
room_data_.keys.pop_back();
|
||||||
|
return std::string("Error: ") + e.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_index = static_cast<int>(room_data_.keys.size()) - 1;
|
||||||
|
selection_ = {EntityType::KEY, new_index};
|
||||||
|
|
||||||
|
autosave();
|
||||||
|
return "Added key " + std::to_string(new_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elimina la llave seleccionada
|
||||||
|
auto MapEditor::deleteKey() -> std::string {
|
||||||
|
if (!active_) { return "Editor not active"; }
|
||||||
|
if (!hasSelectedKey()) { return "No key selected"; }
|
||||||
|
|
||||||
|
const int IDX = selection_.index;
|
||||||
|
room_data_.keys.erase(room_data_.keys.begin() + IDX);
|
||||||
|
|
||||||
|
// Recrear todas las llaves (los índices cambian al borrar)
|
||||||
|
auto* key_mgr = room_->getKeyManager();
|
||||||
|
key_mgr->clear();
|
||||||
|
for (const auto& key_data : room_data_.keys) {
|
||||||
|
key_mgr->addKey(std::make_shared<Key>(key_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
selection_.clear();
|
||||||
|
autosave();
|
||||||
|
return "Deleted key " + std::to_string(IDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplica la llave seleccionada (la pone un tile a la derecha)
|
||||||
|
auto MapEditor::duplicateKey() -> std::string {
|
||||||
|
if (!active_) { return "Editor not active"; }
|
||||||
|
if (!hasSelectedKey()) { return "No key selected"; }
|
||||||
|
|
||||||
|
Key::Data copy = room_data_.keys[selection_.index];
|
||||||
|
copy.x += Tile::SIZE;
|
||||||
|
|
||||||
|
room_data_.keys.push_back(copy);
|
||||||
|
try {
|
||||||
|
room_->getKeyManager()->addKey(std::make_shared<Key>(copy));
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
room_data_.keys.pop_back();
|
||||||
|
return std::string("Error: ") + e.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_index = static_cast<int>(room_data_.keys.size()) - 1;
|
||||||
|
selection_ = {EntityType::KEY, new_index};
|
||||||
|
|
||||||
|
autosave();
|
||||||
|
return "Duplicated as key " + std::to_string(new_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PUERTAS
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Reconstruye todas las puertas vivas desde room_data_, limpiando primero los
|
||||||
|
// WALLs antiguos del CollisionMap. Lo usa setDoorProperty cuando un cambio
|
||||||
|
// (id, animation) requiere recrear el Door y mantener los tiles sincronizados.
|
||||||
|
void MapEditor::rebuildDoors() {
|
||||||
|
auto* door_mgr = room_->getDoorManager();
|
||||||
|
// Borrar una a una desde el principio: cada removeDoor limpia sus WALLs
|
||||||
|
while (door_mgr->getCount() > 0) {
|
||||||
|
door_mgr->removeDoor(0);
|
||||||
|
}
|
||||||
|
// Re-añadir desde room_data_; addDoor reescribe los WALLs si bloquean
|
||||||
|
for (const auto& d : room_data_.doors) {
|
||||||
|
door_mgr->addDoor(std::make_shared<Door>(d, /*start_opened=*/false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifica una propiedad de la puerta seleccionada
|
||||||
|
auto MapEditor::setDoorProperty(const std::string& property, const std::string& value) -> std::string {
|
||||||
|
if (!active_) { return "Editor not active"; }
|
||||||
|
if (!hasSelectedDoor()) { return "No door selected"; }
|
||||||
|
|
||||||
|
auto& door = room_data_.doors[selection_.index];
|
||||||
|
|
||||||
|
if (property == "ID") {
|
||||||
|
door.id = value;
|
||||||
|
// Recrear preserva el id pero pasa por el constructor → rebuild completo
|
||||||
|
// para mantener los tiles del CollisionMap sincronizados.
|
||||||
|
rebuildDoors();
|
||||||
|
autosave();
|
||||||
|
return "id: " + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == "ANIMATION") {
|
||||||
|
std::string anim = toLower(value);
|
||||||
|
if (anim.find('.') == std::string::npos) { anim += ".yaml"; }
|
||||||
|
std::string old_anim = door.animation_path;
|
||||||
|
door.animation_path = anim;
|
||||||
|
try {
|
||||||
|
rebuildDoors();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
door.animation_path = old_anim;
|
||||||
|
// Reconstruir con los datos viejos para dejar el manager coherente
|
||||||
|
rebuildDoors();
|
||||||
|
return std::string("Error: ") + e.what();
|
||||||
|
}
|
||||||
|
autosave();
|
||||||
|
return "animation: " + anim;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown property: " + property + " (use: id, animation)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea una nueva puerta centrada en la habitación, alineada al grid de 8 px
|
||||||
|
auto MapEditor::addDoor() -> std::string {
|
||||||
|
if (!active_) { return "Editor not active"; }
|
||||||
|
|
||||||
|
Door::Data new_door;
|
||||||
|
new_door.animation_path = "door1.yaml";
|
||||||
|
new_door.id = "1";
|
||||||
|
// Centrar y alinear al grid (la puerta ocupa 1×4 tiles)
|
||||||
|
new_door.x = static_cast<float>((PlayArea::CENTER_X / Tile::SIZE) * Tile::SIZE);
|
||||||
|
new_door.y = static_cast<float>((PlayArea::CENTER_Y / Tile::SIZE) * Tile::SIZE);
|
||||||
|
|
||||||
|
room_data_.doors.push_back(new_door);
|
||||||
|
try {
|
||||||
|
// addDoor del manager ya escribe los WALLs si la puerta es bloqueante
|
||||||
|
// (lo es por defecto, porque pasamos start_opened=false).
|
||||||
|
room_->getDoorManager()->addDoor(std::make_shared<Door>(new_door, /*start_opened=*/false));
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
room_data_.doors.pop_back();
|
||||||
|
return std::string("Error: ") + e.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_index = static_cast<int>(room_data_.doors.size()) - 1;
|
||||||
|
selection_ = {EntityType::DOOR, new_index};
|
||||||
|
|
||||||
|
autosave();
|
||||||
|
return "Added door " + std::to_string(new_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elimina la puerta seleccionada (limpia los WALLs del CollisionMap)
|
||||||
|
auto MapEditor::deleteDoor() -> std::string {
|
||||||
|
if (!active_) { return "Editor not active"; }
|
||||||
|
if (!hasSelectedDoor()) { return "No door selected"; }
|
||||||
|
|
||||||
|
const int IDX = selection_.index;
|
||||||
|
|
||||||
|
// Importante: usar removeDoor del manager (limpia los WALLs antes de borrar)
|
||||||
|
room_->getDoorManager()->removeDoor(IDX);
|
||||||
|
room_data_.doors.erase(room_data_.doors.begin() + IDX);
|
||||||
|
|
||||||
|
selection_.clear();
|
||||||
|
autosave();
|
||||||
|
return "Deleted door " + std::to_string(IDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplica la puerta seleccionada (la pone una altura entera abajo, 4 tiles,
|
||||||
|
// para no solapar visualmente)
|
||||||
|
auto MapEditor::duplicateDoor() -> std::string {
|
||||||
|
if (!active_) { return "Editor not active"; }
|
||||||
|
if (!hasSelectedDoor()) { return "No door selected"; }
|
||||||
|
|
||||||
|
Door::Data copy = room_data_.doors[selection_.index];
|
||||||
|
copy.y += 4 * Tile::SIZE; // 4 tiles = altura completa de una puerta
|
||||||
|
|
||||||
|
room_data_.doors.push_back(copy);
|
||||||
|
try {
|
||||||
|
room_->getDoorManager()->addDoor(std::make_shared<Door>(copy, /*start_opened=*/false));
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
room_data_.doors.pop_back();
|
||||||
|
return std::string("Error: ") + e.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_index = static_cast<int>(room_data_.doors.size()) - 1;
|
||||||
|
selection_ = {EntityType::DOOR, new_index};
|
||||||
|
|
||||||
|
autosave();
|
||||||
|
return "Duplicated as door " + std::to_string(new_index);
|
||||||
|
}
|
||||||
|
|
||||||
// Elige un color de grid que contraste con el fondo
|
// Elige un color de grid que contraste con el fondo
|
||||||
// Empieza con bright_black (1), si coincide con el bg en la paleta activa, sube índices
|
// Empieza con bright_black (1), si coincide con el bg en la paleta activa, sube índices
|
||||||
static auto pickGridColor(Uint8 bg, const std::shared_ptr<Surface>& surface) -> Uint8 {
|
static auto pickGridColor(Uint8 bg, const std::shared_ptr<Surface>& surface) -> Uint8 {
|
||||||
|
|||||||
@@ -9,8 +9,10 @@
|
|||||||
|
|
||||||
#include "game/editor/mini_map.hpp" // Para MiniMap
|
#include "game/editor/mini_map.hpp" // Para MiniMap
|
||||||
#include "game/editor/tile_picker.hpp" // Para TilePicker
|
#include "game/editor/tile_picker.hpp" // Para TilePicker
|
||||||
|
#include "game/entities/door.hpp" // Para Door::Data
|
||||||
#include "game/entities/enemy.hpp" // Para Enemy::Data
|
#include "game/entities/enemy.hpp" // Para Enemy::Data
|
||||||
#include "game/entities/item.hpp" // Para Item::Data
|
#include "game/entities/item.hpp" // Para Item::Data
|
||||||
|
#include "game/entities/key.hpp" // Para Key::Data
|
||||||
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
|
#include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data
|
||||||
#include "game/entities/player.hpp" // Para Player::SpawnData
|
#include "game/entities/player.hpp" // Para Player::SpawnData
|
||||||
#include "game/gameplay/room.hpp" // Para Room::Data
|
#include "game/gameplay/room.hpp" // Para Room::Data
|
||||||
@@ -23,7 +25,9 @@ class EditorStatusBar;
|
|||||||
enum class EntityType { NONE,
|
enum class EntityType { NONE,
|
||||||
ENEMY,
|
ENEMY,
|
||||||
ITEM,
|
ITEM,
|
||||||
PLATFORM };
|
PLATFORM,
|
||||||
|
KEY,
|
||||||
|
DOOR };
|
||||||
|
|
||||||
// Seleccion unificada: una sola entidad seleccionada a la vez
|
// Seleccion unificada: una sola entidad seleccionada a la vez
|
||||||
struct Selection {
|
struct Selection {
|
||||||
@@ -90,6 +94,20 @@ class MapEditor {
|
|||||||
auto duplicatePlatform() -> std::string;
|
auto duplicatePlatform() -> std::string;
|
||||||
[[nodiscard]] auto hasSelectedPlatform() const -> bool { return selection_.is(EntityType::PLATFORM); }
|
[[nodiscard]] auto hasSelectedPlatform() const -> bool { return selection_.is(EntityType::PLATFORM); }
|
||||||
|
|
||||||
|
// Comandos para llaves
|
||||||
|
auto setKeyProperty(const std::string& property, const std::string& value) -> std::string;
|
||||||
|
auto addKey() -> std::string;
|
||||||
|
auto deleteKey() -> std::string;
|
||||||
|
auto duplicateKey() -> std::string;
|
||||||
|
[[nodiscard]] auto hasSelectedKey() const -> bool { return selection_.is(EntityType::KEY); }
|
||||||
|
|
||||||
|
// Comandos para puertas
|
||||||
|
auto setDoorProperty(const std::string& property, const std::string& value) -> std::string;
|
||||||
|
auto addDoor() -> std::string;
|
||||||
|
auto deleteDoor() -> std::string;
|
||||||
|
auto duplicateDoor() -> std::string;
|
||||||
|
[[nodiscard]] auto hasSelectedDoor() const -> bool { return selection_.is(EntityType::DOOR); }
|
||||||
|
|
||||||
// Seleccion unificada
|
// Seleccion unificada
|
||||||
[[nodiscard]] auto getSelectionType() const -> EntityType { return selection_.type; }
|
[[nodiscard]] auto getSelectionType() const -> EntityType { return selection_.type; }
|
||||||
|
|
||||||
@@ -136,6 +154,12 @@ class MapEditor {
|
|||||||
void renderGrid() const;
|
void renderGrid() const;
|
||||||
void handleMouseDown(float game_x, float game_y);
|
void handleMouseDown(float game_x, float game_y);
|
||||||
void handleMouseUp();
|
void handleMouseUp();
|
||||||
|
|
||||||
|
// Reconstruye todas las puertas vivas desde room_data_, limpiando primero
|
||||||
|
// los WALLs antiguos del CollisionMap. Lo usa setDoorProperty cuando un
|
||||||
|
// cambio (id, animation) requiere recrear el Door y mantener los tiles
|
||||||
|
// sincronizados.
|
||||||
|
void rebuildDoors();
|
||||||
void updateDrag();
|
void updateDrag();
|
||||||
auto commitEntityDrag() -> bool;
|
auto commitEntityDrag() -> bool;
|
||||||
void moveEntityVisual();
|
void moveEntityVisual();
|
||||||
|
|||||||
@@ -63,3 +63,13 @@ auto Door::justOpened() -> bool {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// Mueve la puerta a la posición indicada (sprite + collider). NO toca el
|
||||||
|
// CollisionMap — eso es responsabilidad del DoorManager (moveDoor/removeDoor).
|
||||||
|
void Door::setPosition(float x, float y) {
|
||||||
|
sprite_->setPosX(x);
|
||||||
|
sprite_->setPosY(y);
|
||||||
|
collider_ = sprite_->getRect();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ class Door {
|
|||||||
|
|
||||||
void setPaused(bool paused) { is_paused_ = paused; } // Pausa/despausa la animación
|
void setPaused(bool paused) { is_paused_ = paused; } // Pausa/despausa la animación
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
void setPosition(float x, float y); // Mueve sprite y collider en vivo (editor; NO toca CollisionMap)
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<AnimatedSprite> sprite_; // Sprite animado de la puerta
|
std::shared_ptr<AnimatedSprite> sprite_; // Sprite animado de la puerta
|
||||||
SDL_FRect collider_{}; // Rectángulo de colisión
|
SDL_FRect collider_{}; // Rectángulo de colisión
|
||||||
|
|||||||
@@ -30,3 +30,12 @@ void Key::update(float delta_time) {
|
|||||||
auto Key::getPos() const -> SDL_FPoint {
|
auto Key::getPos() const -> SDL_FPoint {
|
||||||
return SDL_FPoint{.x = sprite_->getX(), .y = sprite_->getY()};
|
return SDL_FPoint{.x = sprite_->getX(), .y = sprite_->getY()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// Mueve la llave a la posición indicada (sprite + collider). Solo editor.
|
||||||
|
void Key::setPosition(float x, float y) {
|
||||||
|
sprite_->setPosX(x);
|
||||||
|
sprite_->setPosY(y);
|
||||||
|
collider_ = sprite_->getRect();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ class Key {
|
|||||||
|
|
||||||
void setPaused(bool paused) { is_paused_ = paused; } // Pausa/despausa la animación
|
void setPaused(bool paused) { is_paused_ = paused; } // Pausa/despausa la animación
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
void setPosition(float x, float y); // Mueve sprite y collider en vivo (editor)
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<AnimatedSprite> sprite_; // Sprite animado de la llave
|
std::shared_ptr<AnimatedSprite> sprite_; // Sprite animado de la llave
|
||||||
SDL_FRect collider_{}; // Rectángulo de colisión
|
SDL_FRect collider_{}; // Rectángulo de colisión
|
||||||
|
|||||||
@@ -78,6 +78,40 @@ void DoorManager::tryUnlock(const SDL_FRect& player_rect) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// Mueve una puerta del editor: limpia los WALLs viejos, reposiciona el sprite,
|
||||||
|
// y reescribe los WALLs nuevos si la puerta sigue siendo bloqueante.
|
||||||
|
void DoorManager::moveDoor(int index, float x, float y) {
|
||||||
|
if (index < 0 || index >= static_cast<int>(doors_.size())) { return; }
|
||||||
|
auto& door = doors_[index];
|
||||||
|
|
||||||
|
// Limpiar los WALLs viejos antes de mover
|
||||||
|
if (door->isBlocking()) {
|
||||||
|
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::EMPTY));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reposicionar el sprite y el collider del Door
|
||||||
|
door->setPosition(x, y);
|
||||||
|
|
||||||
|
// Re-escribir los WALLs nuevos en la nueva posición si sigue siendo bloqueante
|
||||||
|
if (door->isBlocking()) {
|
||||||
|
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::WALL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elimina una puerta del editor, limpiando los WALLs antes de borrarla del vector
|
||||||
|
void DoorManager::removeDoor(int index) {
|
||||||
|
if (index < 0 || index >= static_cast<int>(doors_.size())) { return; }
|
||||||
|
auto& door = doors_[index];
|
||||||
|
|
||||||
|
if (door->isBlocking()) {
|
||||||
|
writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::EMPTY));
|
||||||
|
}
|
||||||
|
|
||||||
|
doors_.erase(doors_.begin() + index);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Setea las 4 celdas que ocupa la puerta (1 col × 4 filas) al valor indicado
|
// Setea las 4 celdas que ocupa la puerta (1 col × 4 filas) al valor indicado
|
||||||
void DoorManager::writeDoorTiles(const Door& door, int tile_value) {
|
void DoorManager::writeDoorTiles(const Door& door, int tile_value) {
|
||||||
// Convertir posición en píxeles a coordenadas de tile
|
// Convertir posición en píxeles a coordenadas de tile
|
||||||
|
|||||||
@@ -58,6 +58,26 @@ class DoorManager {
|
|||||||
*/
|
*/
|
||||||
void tryUnlock(const SDL_FRect& player_rect);
|
void tryUnlock(const SDL_FRect& player_rect);
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// --- API para el editor (debug) ---
|
||||||
|
[[nodiscard]] auto getCount() const -> int { return static_cast<int>(doors_.size()); }
|
||||||
|
auto getDoor(int index) -> std::shared_ptr<Door>& { return doors_.at(index); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mueve la puerta indicada a (x, y) en píxeles
|
||||||
|
*
|
||||||
|
* Limpia los WALLs viejos del CollisionMap y, si la puerta sigue siendo
|
||||||
|
* bloqueante, escribe los nuevos. Encapsula el bookkeeping de tiles para
|
||||||
|
* que el editor nunca toque el CollisionMap directamente.
|
||||||
|
*/
|
||||||
|
void moveDoor(int index, float x, float y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Elimina la puerta indicada del vector y limpia sus WALLs del CollisionMap
|
||||||
|
*/
|
||||||
|
void removeDoor(int index);
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int DOOR_TILES_HEIGHT = 4; // Una puerta ocupa 4 tiles verticalmente
|
static constexpr int DOOR_TILES_HEIGHT = 4; // Una puerta ocupa 4 tiles verticalmente
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ class KeyManager {
|
|||||||
*/
|
*/
|
||||||
auto checkCollision(SDL_FRect& rect) -> bool;
|
auto checkCollision(SDL_FRect& rect) -> bool;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// --- API para el editor (debug) ---
|
||||||
|
[[nodiscard]] auto getCount() const -> int { return static_cast<int>(keys_.size()); }
|
||||||
|
auto getKey(int index) -> std::shared_ptr<Key>& { return keys_.at(index); }
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::shared_ptr<Key>> keys_; // Colección de llaves
|
std::vector<std::shared_ptr<Key>> keys_; // Colección de llaves
|
||||||
std::string room_id_; // Identificador de la habitación
|
std::string room_id_; // Identificador de la habitación
|
||||||
|
|||||||
@@ -747,6 +747,10 @@ static auto cmdSet(const std::vector<std::string>& args) -> std::string {
|
|||||||
return MapEditor::get()->setItemProperty(args[0], args[1]);
|
return MapEditor::get()->setItemProperty(args[0], args[1]);
|
||||||
case EntityType::PLATFORM:
|
case EntityType::PLATFORM:
|
||||||
return MapEditor::get()->setPlatformProperty(args[0], args[1]);
|
return MapEditor::get()->setPlatformProperty(args[0], args[1]);
|
||||||
|
case EntityType::KEY:
|
||||||
|
return MapEditor::get()->setKeyProperty(args[0], args[1]);
|
||||||
|
case EntityType::DOOR:
|
||||||
|
return MapEditor::get()->setDoorProperty(args[0], args[1]);
|
||||||
default:
|
default:
|
||||||
return MapEditor::get()->setRoomProperty(args[0], args[1]);
|
return MapEditor::get()->setRoomProperty(args[0], args[1]);
|
||||||
}
|
}
|
||||||
@@ -799,6 +803,38 @@ static auto cmdPlatform(const std::vector<std::string>& args) -> std::string {
|
|||||||
}
|
}
|
||||||
return "usage: platform <add|delete|duplicate>";
|
return "usage: platform <add|delete|duplicate>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KEY [ADD|DELETE|DUPLICATE]
|
||||||
|
static auto cmdKey(const std::vector<std::string>& args) -> std::string {
|
||||||
|
if ((MapEditor::get() == nullptr) || !MapEditor::get()->isActive()) { return "Editor not active"; }
|
||||||
|
if (args.empty()) { return "usage: key <add|delete|duplicate>"; }
|
||||||
|
if (args[0] == "ADD") { return MapEditor::get()->addKey(); }
|
||||||
|
if (args[0] == "DELETE") {
|
||||||
|
if (!MapEditor::get()->hasSelectedKey()) { return "No key selected"; }
|
||||||
|
return MapEditor::get()->deleteKey();
|
||||||
|
}
|
||||||
|
if (args[0] == "DUPLICATE") {
|
||||||
|
if (!MapEditor::get()->hasSelectedKey()) { return "No key selected"; }
|
||||||
|
return MapEditor::get()->duplicateKey();
|
||||||
|
}
|
||||||
|
return "usage: key <add|delete|duplicate>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOOR [ADD|DELETE|DUPLICATE]
|
||||||
|
static auto cmdDoor(const std::vector<std::string>& args) -> std::string {
|
||||||
|
if ((MapEditor::get() == nullptr) || !MapEditor::get()->isActive()) { return "Editor not active"; }
|
||||||
|
if (args.empty()) { return "usage: door <add|delete|duplicate>"; }
|
||||||
|
if (args[0] == "ADD") { return MapEditor::get()->addDoor(); }
|
||||||
|
if (args[0] == "DELETE") {
|
||||||
|
if (!MapEditor::get()->hasSelectedDoor()) { return "No door selected"; }
|
||||||
|
return MapEditor::get()->deleteDoor();
|
||||||
|
}
|
||||||
|
if (args[0] == "DUPLICATE") {
|
||||||
|
if (!MapEditor::get()->hasSelectedDoor()) { return "No door selected"; }
|
||||||
|
return MapEditor::get()->duplicateDoor();
|
||||||
|
}
|
||||||
|
return "usage: door <add|delete|duplicate>";
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// SHOW [INFO|NOTIFICATION|CHEEVO]
|
// SHOW [INFO|NOTIFICATION|CHEEVO]
|
||||||
@@ -948,6 +984,8 @@ void CommandRegistry::registerHandlers() { // NOLINT(readability-function-cogni
|
|||||||
handlers_["cmd_enemy"] = cmdEnemy;
|
handlers_["cmd_enemy"] = cmdEnemy;
|
||||||
handlers_["cmd_item"] = cmdItem;
|
handlers_["cmd_item"] = cmdItem;
|
||||||
handlers_["cmd_platform"] = cmdPlatform;
|
handlers_["cmd_platform"] = cmdPlatform;
|
||||||
|
handlers_["cmd_key"] = cmdKey;
|
||||||
|
handlers_["cmd_door"] = cmdDoor;
|
||||||
#endif
|
#endif
|
||||||
// HELP se registra en load() como lambda que captura this
|
// HELP se registra en load() como lambda que captura this
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user