diff --git a/config/assets.yaml b/config/assets.yaml index 169c096..c5006ff 100644 --- a/config/assets.yaml +++ b/config/assets.yaml @@ -96,6 +96,10 @@ assets: path: ${SYSTEM_FOLDER}/debug.yaml required: false absolute: true + - type: DATA + path: ${SYSTEM_FOLDER}/editor.yaml + required: false + absolute: true - type: DATA path: ${SYSTEM_FOLDER}/stats_buffer.csv required: false diff --git a/data/console/commands.yaml b/data/console/commands.yaml index 2e97df4..afeeb3b 100644 --- a/data/console/commands.yaml +++ b/data/console/commands.yaml @@ -215,9 +215,11 @@ categories: - keyword: EDIT handler: cmd_edit description: "Map editor mode (GAME only)" - usage: "EDIT [ON|OFF|REVERT]" + usage: "EDIT [ON|OFF|REVERT|SHOW|HIDE] [INFO|GRID]" completions: - EDIT: [ON, OFF, REVERT] + EDIT: [ON, OFF, REVERT, SHOW, HIDE] + EDIT SHOW: [INFO, GRID] + EDIT HIDE: [INFO, GRID] - keyword: ENEMY handler: cmd_enemy diff --git a/source/core/rendering/surface.hpp b/source/core/rendering/surface.hpp index b467a10..02a20e5 100644 --- a/source/core/rendering/surface.hpp +++ b/source/core/rendering/surface.hpp @@ -139,6 +139,7 @@ class Surface { // Paleta void setPalette(const std::array& palette) { palette_ = palette; } + [[nodiscard]] auto getPaletteColor(Uint8 index) const -> Uint32 { return palette_[index]; } // Inicializa la sub paleta static void initializeSubPalette(SubPalette& palette) { std::iota(palette.begin(), palette.end(), 0); } diff --git a/source/game/editor/map_editor.cpp b/source/game/editor/map_editor.cpp index 0d405c9..b7c3786 100644 --- a/source/game/editor/map_editor.cpp +++ b/source/game/editor/map_editor.cpp @@ -5,9 +5,11 @@ #include #include // Para std::round +#include // Para ifstream, ofstream #include // Para cout #include "core/input/mouse.hpp" // Para Mouse +#include "core/rendering/render_info.hpp" // Para RenderInfo #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/resources/resource_cache.hpp" // Para Resource::Cache @@ -40,11 +42,64 @@ auto MapEditor::get() -> MapEditor* { } // Constructor -MapEditor::MapEditor() = default; +MapEditor::MapEditor() { + loadSettings(); +} // Destructor MapEditor::~MapEditor() = default; +// Carga las opciones del editor desde editor.yaml +void MapEditor::loadSettings() { + std::string path = Resource::List::get()->get("editor.yaml"); + if (path.empty()) { return; } + + std::ifstream file(path); + if (!file.is_open()) { return; } + + std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + try { + auto yaml = fkyaml::node::deserialize(content); + if (yaml.contains("grid")) { settings_.grid = yaml["grid"].get_value(); } + if (yaml.contains("show_render_info")) { settings_.show_render_info = yaml["show_render_info"].get_value(); } + } catch (...) { + // Fichero corrupto o vacío, usar defaults + } +} + +// Guarda las opciones del editor a editor.yaml +void MapEditor::saveSettings() { + std::string path = Resource::List::get()->get("editor.yaml"); + if (path.empty()) { return; } + + std::ofstream file(path); + if (!file.is_open()) { return; } + + file << "# Map Editor Settings\n"; + file << "grid: " << (settings_.grid ? "true" : "false") << "\n"; + file << "show_render_info: " << (settings_.show_render_info ? "true" : "false") << "\n"; + file.close(); +} + +// Muestra/oculta render info (persistente) +auto MapEditor::showInfo(bool show) -> std::string { + settings_.show_render_info = show; + if (RenderInfo::get()->isActive() != show) { + RenderInfo::get()->toggle(); + } + saveSettings(); + return show ? "Info ON" : "Info OFF"; +} + +// Muestra/oculta grid (persistente) +auto MapEditor::showGrid(bool show) -> std::string { + settings_.grid = show; + saveSettings(); + return show ? "Grid ON" : "Grid OFF"; +} + // Entra en modo editor void MapEditor::enter(std::shared_ptr room, std::shared_ptr player, const std::string& room_path, std::shared_ptr scoreboard_data) { if (active_) { return; } @@ -72,6 +127,12 @@ void MapEditor::enter(std::shared_ptr room, std::shared_ptr player Options::cheats.invincible = Options::Cheat::State::ENABLED; player_->setColor(); + // Guardar estado de render_info y aplicar el setting del editor + render_info_before_editor_ = RenderInfo::get()->isActive(); + if (settings_.show_render_info != render_info_before_editor_) { + RenderInfo::get()->toggle(); + } + // Resetear enemigos a su posición inicial (pueden haberse movido durante el gameplay) room_->resetEnemyPositions(room_data_.enemies); @@ -99,6 +160,11 @@ void MapEditor::exit() { Options::cheats.invincible = invincible_before_editor_; player_->setColor(); + // Restaurar render_info + if (RenderInfo::get()->isActive() != render_info_before_editor_) { + RenderInfo::get()->toggle(); + } + // Restaurar prompt de la consola y limpiar estado selected_enemy_ = -1; Console::get()->setPrompt("> "); @@ -203,6 +269,11 @@ void MapEditor::update(float delta_time) { void MapEditor::render() { // El tilemap ya ha sido renderizado por Game::renderPlaying() antes de llamar aquí + // Grid (debajo de todo) + if (settings_.grid) { + renderGrid(); + } + // Renderizar los marcadores de boundaries y líneas de ruta (debajo de los sprites) renderEnemyBoundaries(); @@ -1136,4 +1207,40 @@ auto MapEditor::duplicateItem() -> std::string { return "Duplicated as item " + std::to_string(selected_item_); } +// 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 +static auto pickGridColor(Uint8 bg, const std::shared_ptr& surface) -> Uint8 { + Uint8 grid = static_cast(PaletteColor::BRIGHT_BLACK); + Uint32 bg_argb = surface->getPaletteColor(bg); + + // Si bright_black es igual al bg, buscar el siguiente color distinto + while (grid < 15 && surface->getPaletteColor(grid) == bg_argb) { + grid += 2; // Saltar de 2 en 2 para mantenerse en tonos discretos + } + + return grid; +} + +// Dibuja la cuadrícula de tiles (líneas de puntos, 1 pixel sí / 1 no) +void MapEditor::renderGrid() { + auto game_surface = Screen::get()->getRendererSurface(); + if (!game_surface) { return; } + + const Uint8 COLOR = pickGridColor(stringToColor(room_data_.bg_color), game_surface); + + // Líneas verticales (cada 8 pixels) + for (int x = Tile::SIZE; x < PlayArea::WIDTH; x += Tile::SIZE) { + for (int y = 0; y < PlayArea::HEIGHT; y += 2) { + game_surface->putPixel(x, y, COLOR); + } + } + + // Líneas horizontales (cada 8 pixels) + for (int y = Tile::SIZE; y < PlayArea::HEIGHT; y += Tile::SIZE) { + for (int x = 0; x < PlayArea::WIDTH; x += 2) { + game_surface->putPixel(x, y, COLOR); + } + } +} + #endif // _DEBUG diff --git a/source/game/editor/map_editor.hpp b/source/game/editor/map_editor.hpp index 7164ea3..f156f3d 100644 --- a/source/game/editor/map_editor.hpp +++ b/source/game/editor/map_editor.hpp @@ -43,6 +43,11 @@ class MapEditor { // Comandos para propiedades de la habitación auto setRoomProperty(const std::string& property, const std::string& value) -> std::string; + // Opciones del editor (llamados desde console_commands / teclas) + auto showInfo(bool show) -> std::string; + auto showGrid(bool show) -> std::string; + [[nodiscard]] auto isGridEnabled() const -> bool { return settings_.grid; } + // Comandos para items auto setItemProperty(const std::string& property, const std::string& value) -> std::string; auto addItem() -> std::string; @@ -57,6 +62,15 @@ class MapEditor { MapEditor(); // Constructor ~MapEditor(); // Destructor + // Opciones persistentes del editor + struct Settings { + bool grid{false}; + bool show_render_info{false}; + }; + Settings settings_; + void loadSettings(); + void saveSettings(); + // Tipos para drag & drop y selección enum class DragTarget { NONE, PLAYER, @@ -80,6 +94,7 @@ class MapEditor { void renderEnemyBoundaries(); void renderBoundaryMarker(float x, float y, Uint8 color); void renderSelectionHighlight(); + void renderGrid(); void handleMouseDown(float game_x, float game_y); void handleMouseUp(); void updateDrag(); @@ -124,8 +139,9 @@ class MapEditor { int mouse_tile_x_{0}; int mouse_tile_y_{0}; - // Estado previo de invencibilidad + // Estado previo (para restaurar al salir) Options::Cheat::State invincible_before_editor_{Options::Cheat::State::DISABLED}; + bool render_info_before_editor_{false}; }; #endif // _DEBUG diff --git a/source/game/scenes/game.cpp b/source/game/scenes/game.cpp index 680d1a1..295d1f8 100644 --- a/source/game/scenes/game.cpp +++ b/source/game/scenes/game.cpp @@ -191,6 +191,8 @@ void Game::handleEvents() { GameControl::enter_editor(); Notifier::get()->show({Locale::get()->get("game.editor_enabled")}); // NOLINT(readability-static-accessed-through-instance) } + } else if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_8 && static_cast(event.key.repeat) == 0 && MapEditor::get()->isActive()) { + MapEditor::get()->showGrid(!MapEditor::get()->isGridEnabled()); } else if (MapEditor::get()->isActive()) { MapEditor::get()->handleEvent(event); } else { diff --git a/source/game/ui/console_commands.cpp b/source/game/ui/console_commands.cpp index b3093ab..8a9929c 100644 --- a/source/game/ui/console_commands.cpp +++ b/source/game/ui/console_commands.cpp @@ -702,7 +702,14 @@ static auto cmd_edit(const std::vector& args) -> std::string { } return "Editor not active"; } - return "usage: edit [on|off|revert]"; + // EDIT SHOW/HIDE INFO/GRID + if ((args[0] == "SHOW" || args[0] == "HIDE") && args.size() >= 2) { + if (!MapEditor::get() || !MapEditor::get()->isActive()) { return "Editor not active"; } + bool show = (args[0] == "SHOW"); + if (args[1] == "INFO") { return MapEditor::get()->showInfo(show); } + if (args[1] == "GRID") { return MapEditor::get()->showGrid(show); } + } + return "usage: edit [on|off|revert|show|hide] [info|grid]"; } // SET — modifica propiedad del enemigo seleccionado o de la habitación