diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bf5bbd..a898255 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ set(APP_SOURCES # Game - Editor (debug only, guarded by #ifdef _DEBUG in source) source/game/editor/map_editor.cpp source/game/editor/editor_statusbar.cpp + source/game/editor/room_saver.cpp # Game - UI source/game/ui/console.cpp diff --git a/data/console/commands.yaml b/data/console/commands.yaml index 3da10a8..6179a50 100644 --- a/data/console/commands.yaml +++ b/data/console/commands.yaml @@ -215,9 +215,9 @@ categories: - keyword: EDIT handler: cmd_edit description: "Map editor mode (GAME only)" - usage: "EDIT [ON|OFF|SAVE]" + usage: "EDIT [ON|OFF|REVERT]" completions: - EDIT: [ON, OFF, SAVE] + EDIT: [ON, OFF, REVERT] - name: CHEATS commands: diff --git a/data/room/03.yaml b/data/room/03.yaml index dfbd896..75b531c 100644 --- a/data/room/03.yaml +++ b/data/room/03.yaml @@ -1,58 +1,568 @@ -# VOID MAIN +enemies: + - + animation: code.yaml + boundaries: + position1: + x: 5 + y: 10 + position2: + x: 14 + y: 10 + color: yellow + position: + x: 12 + y: 10 + velocity: + x: 24.0 + y: 0 +items: + - + counter: 1 + position: + x: 21 + y: 13 + tile: 42 + tileSetFile: items.gif room: - name_en: "VOID MAIN" - name_ca: "VOID MAIN" bgColor: black border: magenta - tileSetFile: standard.gif - - # Conexiones de la habitación (null = sin conexión) connections: - up: null down: null left: 02.yaml right: null - - # Colores de los objetos + up: null + conveyorBelt: left itemColor1: bright_cyan itemColor2: yellow - - # Dirección de la cinta transportadora: left, none, right - conveyorBelt: left - -# Tilemap: 16 filas × 32 columnas (256×192 píxeles @ 8px/tile) -# Índices de tiles (-1 = vacío) + name_ca: VOID MAIN + name_en: VOID MAIN + tileSetFile: standard.gif tilemap: - - [24, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 24] - - [24, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 24] - - [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 24] - - [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 24] - - [24, 24, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, -1, -1, 504, 24, 24, 24, 24, 24, 24, 24, 24] - - [24, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 504, -1, 24, -1, -1, -1, -1, -1, 24, 24] - - [24, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 504, -1, -1, 24, -1, -1, -1, -1, -1, 24, 24] - - [24, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 504, -1, -1, -1, 24, -1, -1, -1, -1, -1, 24, 24] - - [24, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 504, -1, -1, -1, -1, 24, -1, -1, -1, -1, -1, 24, 24] - - [24, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 504, -1, -1, -1, -1, -1, 24, -1, -1, -1, -1, -1, 24, 24] - - [24, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 504, -1, -1, -1, -1, -1, -1, 24, -1, -1, -1, -1, -1, 24, 24] - - [24, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 504, -1, -1, -1, -1, -1, -1, -1, 24, 24, 24, -1, -1, -1, 24, 24] - - [24, 24, -1, -1, -1, -1, -1, -1, -1, 252, 252, 252, 252, 252, 252, 252, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 24] - - [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 134, 24] - - [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 440, 440, 440, 440, -1, -1, -1, -1, -1, -1, -1, -1, 134, 24] - - [24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24] - -# Enemigos en esta habitación -enemies: - - animation: code.yaml - position: {x: 3, y: 2} - velocity: {x: 24.0, y: 0} - boundaries: - position1: {x: 3, y: 2} - position2: {x: 27, y: 2} - color: yellow - -# Objetos en esta habitación -items: - - tileSetFile: items.gif - tile: 42 - position: {x: 21, y: 13} - counter: 1 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - 24 + - 24 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - -1 + - -1 + - 504 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 504 + - -1 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 504 + - -1 + - -1 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 504 + - -1 + - -1 + - -1 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 504 + - -1 + - -1 + - -1 + - -1 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 504 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 504 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 504 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - 24 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - 24 + - 24 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - 252 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 24 + - 24 + - + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 134 + - 24 + - + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 440 + - 440 + - 440 + - 440 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - -1 + - 134 + - 24 + - + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 + - 24 diff --git a/source/game/editor/map_editor.cpp b/source/game/editor/map_editor.cpp index d83d5a4..8fdc06c 100644 --- a/source/game/editor/map_editor.cpp +++ b/source/game/editor/map_editor.cpp @@ -11,7 +11,9 @@ #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/resources/resource_cache.hpp" // Para Resource::Cache +#include "core/resources/resource_list.hpp" // Para Resource::List #include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar +#include "game/editor/room_saver.hpp" // Para RoomSaver #include "game/entities/player.hpp" // Para Player #include "game/gameplay/enemy_manager.hpp" // Para EnemyManager #include "game/gameplay/item_manager.hpp" // Para ItemManager @@ -51,12 +53,19 @@ void MapEditor::enter(std::shared_ptr room, std::shared_ptr player room_path_ = room_path; scoreboard_data_ = std::move(scoreboard_data); - // Cargar una copia de los datos de la habitación (para boundaries y edición futura) + // Cargar una copia de los datos de la habitación (para boundaries y edición) auto room_data_ptr = Resource::Cache::get()->getRoom(room_path); if (room_data_ptr) { room_data_ = *room_data_ptr; } + // Obtener la ruta completa y cargar el YAML original (para edición parcial y backup) + file_path_ = Resource::List::get()->get(room_path_); + if (!file_path_.empty()) { + yaml_ = RoomSaver::loadYAML(file_path_); + yaml_backup_ = yaml_; // Copia profunda para revert + } + // Guardar estado de invencibilidad y forzarla invincible_before_editor_ = Options::cheats.invincible; Options::cheats.invincible = Options::Cheat::State::ENABLED; @@ -95,6 +104,39 @@ void MapEditor::exit() { std::cout << "MapEditor: OFF\n"; } +// Revierte todos los cambios al estado original +auto MapEditor::revert() -> std::string { + if (!active_) { return "Editor not active"; } + if (file_path_.empty()) { return "Error: No file path"; } + + // Restaurar el YAML al backup original + yaml_ = yaml_backup_; + RoomSaver::saveYAML(file_path_, yaml_); + + // Recargar room_data_ desde el backup + auto room_data_ptr = Resource::Cache::get()->getRoom(room_path_); + if (room_data_ptr) { + room_data_ = *room_data_ptr; + } + + // Resetear los sprites vivos a las posiciones originales + room_->resetEnemyPositions(room_data_.enemies); + + // Resetear items (recargar posiciones) + auto* item_mgr = room_->getItemManager(); + for (int i = 0; i < item_mgr->getCount() && i < static_cast(room_data_.items.size()); ++i) { + item_mgr->getItem(i)->setPosition(room_data_.items[i].x, room_data_.items[i].y); + } + + return "Reverted to original"; +} + +// Auto-guarda los cambios puntuales al YAML tras soltar una entidad +void MapEditor::autosave() { + if (file_path_.empty()) { return; } + RoomSaver::saveYAML(file_path_, yaml_); +} + // Actualiza el editor void MapEditor::update(float delta_time) { // Mantener el ratón siempre visible @@ -242,18 +284,22 @@ void MapEditor::handleMouseUp() { const int SNAP_X = static_cast(drag_.snap_x); const int SNAP_Y = static_cast(drag_.snap_y); + bool changed = false; + switch (drag_.target) { case DragTarget::PLAYER: player_->setDebugPosition(drag_.snap_x, drag_.snap_y); player_->finalizeDebugTeleport(); + // El jugador no se guarda en el YAML de la habitación (es dato de spawn global) break; case DragTarget::ENEMY_INITIAL: - // Actualizar datos mutables y posición del sprite vivo if (IDX >= 0 && IDX < static_cast(room_data_.enemies.size())) { room_data_.enemies[IDX].x = drag_.snap_x; room_data_.enemies[IDX].y = drag_.snap_y; room_->getEnemyManager()->getEnemy(IDX)->resetToInitialPosition(room_data_.enemies[IDX]); + RoomSaver::updateEnemyPosition(yaml_, IDX, drag_.snap_x, drag_.snap_y); + changed = true; } break; @@ -261,6 +307,8 @@ void MapEditor::handleMouseUp() { if (IDX >= 0 && IDX < static_cast(room_data_.enemies.size())) { room_data_.enemies[IDX].x1 = SNAP_X; room_data_.enemies[IDX].y1 = SNAP_Y; + RoomSaver::updateEnemyBound1(yaml_, IDX, SNAP_X, SNAP_Y); + changed = true; } break; @@ -268,12 +316,16 @@ void MapEditor::handleMouseUp() { if (IDX >= 0 && IDX < static_cast(room_data_.enemies.size())) { room_data_.enemies[IDX].x2 = SNAP_X; room_data_.enemies[IDX].y2 = SNAP_Y; + RoomSaver::updateEnemyBound2(yaml_, IDX, SNAP_X, SNAP_Y); + changed = true; } break; case DragTarget::ITEM: if (IDX >= 0 && IDX < room_->getItemManager()->getCount()) { room_->getItemManager()->getItem(IDX)->setPosition(drag_.snap_x, drag_.snap_y); + RoomSaver::updateItemPosition(yaml_, IDX, drag_.snap_x, drag_.snap_y); + changed = true; } break; @@ -281,6 +333,11 @@ void MapEditor::handleMouseUp() { break; } + // Auto-guardar si hubo cambio + if (changed) { + autosave(); + } + // Resetear estado de drag drag_ = {}; } diff --git a/source/game/editor/map_editor.hpp b/source/game/editor/map_editor.hpp index 187d2a7..34e5368 100644 --- a/source/game/editor/map_editor.hpp +++ b/source/game/editor/map_editor.hpp @@ -7,6 +7,7 @@ #include // Para shared_ptr, unique_ptr #include // Para string +#include "external/fkyaml_node.hpp" // Para fkyaml::node #include "game/entities/enemy.hpp" // Para Enemy::Data #include "game/entities/item.hpp" // Para Item::Data #include "game/entities/player.hpp" // Para Player::SpawnData @@ -29,6 +30,7 @@ class MapEditor { void update(float delta_time); void render(); void handleEvent(const SDL_Event& event); + auto revert() -> std::string; // Revierte todos los cambios al estado original private: static MapEditor* instance_; // [SINGLETON] Objeto privado @@ -59,8 +61,9 @@ class MapEditor { void renderBoundaryMarker(float x, float y, Uint8 color); // Dibuja un marcador de boundary en una posición void renderSelectionHighlight(); // Dibuja highlight del elemento seleccionado/arrastrado void handleMouseDown(float game_x, float game_y); // Procesa click del ratón (hit test + inicio de drag) - void handleMouseUp(); // Procesa soltar el ratón (commit del drag) + void handleMouseUp(); // Procesa soltar el ratón (commit del drag + autosave) void updateDrag(); // Actualiza la posición snapped durante el drag + void autosave(); // Guarda los cambios puntuales al YAML static auto snapToGrid(float value) -> float; // Alinea un valor a la cuadrícula de 8x8 static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool; // Hit test punto en rectángulo @@ -68,10 +71,14 @@ class MapEditor { bool active_{false}; DragState drag_; - // Copia mutable de los datos de la habitación (para edición futura) - Room::Data room_data_; - Player::SpawnData player_spawn_; - std::string room_path_; + // Datos de la habitación + Room::Data room_data_; // Copia mutable (para boundaries y edición) + std::string room_path_; // Nombre del fichero (ej: "06.yaml") + std::string file_path_; // Ruta completa del fichero en disco + + // YAML: nodo vivo (se edita parcialmente) y backup para revert + fkyaml::node yaml_; // Nodo YAML actual (editado parcialmente) + fkyaml::node yaml_backup_; // Backup del YAML original al entrar // Referencias a objetos vivos (para rendering) std::shared_ptr room_; diff --git a/source/game/editor/room_saver.cpp b/source/game/editor/room_saver.cpp new file mode 100644 index 0000000..6e4c88a --- /dev/null +++ b/source/game/editor/room_saver.cpp @@ -0,0 +1,118 @@ +#ifdef _DEBUG + +#include "game/editor/room_saver.hpp" + +#include // Para std::round +#include // Para ofstream +#include // Para cout, cerr + +#include "core/resources/resource_helper.hpp" // Para Resource::Helper +#include "utils/defines.hpp" // Para Tile::SIZE + +// Carga el YAML original desde disco +auto RoomSaver::loadYAML(const std::string& file_path) -> fkyaml::node { + auto file_data = Resource::Helper::loadFile(file_path); + if (file_data.empty()) { + std::cerr << "RoomSaver: Cannot load " << file_path << "\n"; + return {}; + } + + std::string content(file_data.begin(), file_data.end()); + return fkyaml::node::deserialize(content); +} + +// Guarda el nodo YAML completo a disco +auto RoomSaver::saveYAML(const std::string& file_path, const fkyaml::node& yaml) -> std::string { + std::string content = fkyaml::node::serialize(yaml); + + std::ofstream file(file_path); + if (!file.is_open()) { + std::cerr << "RoomSaver: Cannot write to " << file_path << "\n"; + return "Error: Cannot write to " + file_path; + } + + file << content; + file.close(); + + const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1); + std::cout << "RoomSaver: Saved " << FILE_NAME << "\n"; + return "Saved " + FILE_NAME; +} + +// Actualiza la posición inicial de un enemigo (pixels → tiles) +void RoomSaver::updateEnemyPosition(fkyaml::node& yaml, int index, float x, float y) { + if (!yaml.contains("enemies")) { return; } + auto& enemies = yaml["enemies"]; + if (index < 0 || index >= static_cast(enemies.size())) { return; } + + auto& enemy = enemies[index]; + int tile_x = static_cast(std::round(x / Tile::SIZE)); + int tile_y = static_cast(std::round(y / Tile::SIZE)); + + if (!enemy.contains("position")) { + enemy["position"] = fkyaml::node::mapping(); + } + enemy["position"]["x"] = tile_x; + enemy["position"]["y"] = tile_y; +} + +// Actualiza boundary1 de un enemigo (pixels → tiles) +void RoomSaver::updateEnemyBound1(fkyaml::node& yaml, int index, int x, int y) { + if (!yaml.contains("enemies")) { return; } + auto& enemies = yaml["enemies"]; + if (index < 0 || index >= static_cast(enemies.size())) { return; } + + auto& enemy = enemies[index]; + int tile_x = x / Tile::SIZE; + int tile_y = y / Tile::SIZE; + + if (!enemy.contains("boundaries")) { + enemy["boundaries"] = fkyaml::node::mapping(); + } + auto& bounds = enemy["boundaries"]; + if (!bounds.contains("position1")) { + bounds["position1"] = fkyaml::node::mapping(); + } + bounds["position1"]["x"] = tile_x; + bounds["position1"]["y"] = tile_y; +} + +// Actualiza boundary2 de un enemigo (pixels → tiles) +void RoomSaver::updateEnemyBound2(fkyaml::node& yaml, int index, int x, int y) { + if (!yaml.contains("enemies")) { return; } + auto& enemies = yaml["enemies"]; + if (index < 0 || index >= static_cast(enemies.size())) { return; } + + auto& enemy = enemies[index]; + int tile_x = x / Tile::SIZE; + int tile_y = y / Tile::SIZE; + + if (!enemy.contains("boundaries")) { + enemy["boundaries"] = fkyaml::node::mapping(); + } + auto& bounds = enemy["boundaries"]; + if (!bounds.contains("position2")) { + bounds["position2"] = fkyaml::node::mapping(); + } + bounds["position2"]["x"] = tile_x; + bounds["position2"]["y"] = tile_y; +} + +// Actualiza la posición de un item (pixels → tiles) +void RoomSaver::updateItemPosition(fkyaml::node& yaml, int index, float x, float y) { + if (!yaml.contains("items")) { return; } + auto& items = yaml["items"]; + if (index < 0 || index >= static_cast(items.size())) { return; } + + auto& item = items[index]; + int tile_x = static_cast(std::round(x / Tile::SIZE)); + int tile_y = static_cast(std::round(y / Tile::SIZE)); + + if (!item.contains("position")) { + item["position"] = fkyaml::node::mapping(); + } + item["position"]["x"] = tile_x; + item["position"]["y"] = tile_y; +} + +#endif // _DEBUG diff --git a/source/game/editor/room_saver.hpp b/source/game/editor/room_saver.hpp new file mode 100644 index 0000000..f76cb2e --- /dev/null +++ b/source/game/editor/room_saver.hpp @@ -0,0 +1,36 @@ +#pragma once + +#ifdef _DEBUG + +#include // Para string + +#include "external/fkyaml_node.hpp" // Para fkyaml::node +#include "game/entities/enemy.hpp" // Para Enemy::Data +#include "game/entities/item.hpp" // Para Item::Data +#include "game/entities/player.hpp" // Para Player::SpawnData + +/** + * @brief Edición parcial de archivos YAML de habitaciones + * + * Lee el YAML original, modifica solo los campos editados y serializa. + * Preserva todos los campos que no se editan (name_ca, comentarios del formato, etc.) + * Solo se usa en builds de debug (editor de mapas). + */ +class RoomSaver { + public: + RoomSaver() = delete; + + // Carga el YAML original desde disco (llamar al entrar al editor) + static auto loadYAML(const std::string& file_path) -> fkyaml::node; + + // Guarda el nodo YAML completo a disco + static auto saveYAML(const std::string& file_path, const fkyaml::node& yaml) -> std::string; + + // Modificaciones puntuales sobre el nodo YAML (posiciones en pixels, se convierten a tiles) + static void updateEnemyPosition(fkyaml::node& yaml, int index, float x, float y); + static void updateEnemyBound1(fkyaml::node& yaml, int index, int x, int y); + static void updateEnemyBound2(fkyaml::node& yaml, int index, int x, int y); + static void updateItemPosition(fkyaml::node& yaml, int index, float x, float y); +}; + +#endif // _DEBUG diff --git a/source/game/game_control.hpp b/source/game/game_control.hpp index ecefc45..84c0124 100644 --- a/source/game/game_control.hpp +++ b/source/game/game_control.hpp @@ -26,6 +26,6 @@ namespace GameControl { // Registradas por Game::Game() — control del editor de mapas inline std::function enter_editor; inline std::function exit_editor; - inline std::function save_editor; + inline std::function revert_editor; } // namespace GameControl #endif diff --git a/source/game/scenes/game.cpp b/source/game/scenes/game.cpp index 976d49e..4bbe1f2 100644 --- a/source/game/scenes/game.cpp +++ b/source/game/scenes/game.cpp @@ -126,7 +126,9 @@ Game::Game(Mode mode) GameControl::exit_editor = []() -> void { MapEditor::get()->exit(); }; - GameControl::save_editor = nullptr; // Se implementará en la fase 5 + GameControl::revert_editor = []() -> std::string { + return MapEditor::get()->revert(); + }; #endif SceneManager::current = (mode_ == Mode::GAME) ? SceneManager::Scene::GAME : SceneManager::Scene::DEMO; @@ -150,7 +152,7 @@ Game::~Game() { if (MapEditor::get()->isActive()) { MapEditor::get()->exit(); } GameControl::enter_editor = nullptr; GameControl::exit_editor = nullptr; - GameControl::save_editor = nullptr; + GameControl::revert_editor = nullptr; #endif } diff --git a/source/game/ui/console_commands.cpp b/source/game/ui/console_commands.cpp index 1e43f67..26874ca 100644 --- a/source/game/ui/console_commands.cpp +++ b/source/game/ui/console_commands.cpp @@ -630,7 +630,7 @@ static auto cmd_scene(const std::vector& args) -> std::string { return "Unknown scene: " + args[0]; } -// EDIT [ON|OFF|SAVE] +// EDIT [ON|OFF|REVERT] static auto cmd_edit(const std::vector& args) -> std::string { if (args.empty() || args[0] == "ON") { if (GameControl::enter_editor) { @@ -646,13 +646,13 @@ static auto cmd_edit(const std::vector& args) -> std::string { } return "Not in game"; } - if (args[0] == "SAVE") { - if (GameControl::save_editor) { - return GameControl::save_editor(); + if (args[0] == "REVERT") { + if (GameControl::revert_editor) { + return GameControl::revert_editor(); } return "Editor not active"; } - return "usage: edit [on|off|save]"; + return "usage: edit [on|off|revert]"; } #endif