diff --git a/source/game/editor/map_editor.cpp b/source/game/editor/map_editor.cpp index 3b9ebaf..8570596 100644 --- a/source/game/editor/map_editor.cpp +++ b/source/game/editor/map_editor.cpp @@ -15,6 +15,7 @@ #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/game_control.hpp" // Para GameControl #include "game/editor/room_saver.hpp" // Para RoomSaver #include "game/entities/player.hpp" // Para Player #include "game/gameplay/enemy_manager.hpp" // Para EnemyManager @@ -119,8 +120,19 @@ static auto parseColor(const std::string& value) -> Uint8 { void MapEditor::toggleMiniMap() { if (!mini_map_) { mini_map_ = std::make_unique(parseColor(settings_.minimap_bg), parseColor(settings_.minimap_conn)); + mini_map_->on_navigate = [this](const std::string& room_name) { + mini_map_visible_ = false; + // Navegar a la room (usa changeRoomWithEditor vía GameControl) + if (GameControl::exit_editor) { GameControl::exit_editor(); } + if (GameControl::change_room && GameControl::change_room(room_name)) { + if (GameControl::enter_editor) { GameControl::enter_editor(); } + } + }; } mini_map_visible_ = !mini_map_visible_; + if (mini_map_visible_) { + mini_map_->centerOnRoom(room_path_); + } } auto MapEditor::setMiniMapBg(const std::string& color) -> std::string { @@ -347,18 +359,16 @@ void MapEditor::handleEvent(const SDL_Event& event) { return; } - // Si el mini mapa está visible, cerrarlo con click o ESC - if (mini_map_visible_) { - if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN || - (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE)) { + // Si el mini mapa está visible, delegar eventos (ESC o M para cerrar) + if (mini_map_visible_ && mini_map_) { + if (event.type == SDL_EVENT_KEY_DOWN && + (event.key.key == SDLK_ESCAPE || event.key.key == SDLK_M) && + static_cast(event.key.repeat) == 0) { mini_map_visible_ = false; return; } - if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_M) { - mini_map_visible_ = false; - return; - } - return; // Bloquear otros eventos mientras el minimapa está visible + mini_map_->handleEvent(event, room_path_); + return; } // ESC: desactivar brush diff --git a/source/game/editor/mini_map.cpp b/source/game/editor/mini_map.cpp index 0ca063d..8b83a48 100644 --- a/source/game/editor/mini_map.cpp +++ b/source/game/editor/mini_map.cpp @@ -3,6 +3,7 @@ #include "game/editor/mini_map.hpp" #include // Para std::max, std::min +#include // Para std::floor #include // Para std::array #include // Para cout #include // Para std::map @@ -37,6 +38,7 @@ void MiniMap::buildTileColorTable(const std::string& tileset_name) { if (!tileset) { return; } tileset_width_ = static_cast(tileset->getWidth()) / Tile::SIZE; + tileset_transparent_ = tileset->getTransparentColor(); int tileset_height = static_cast(tileset->getHeight()) / Tile::SIZE; int total_tiles = tileset_width_ * tileset_height; @@ -46,21 +48,23 @@ void MiniMap::buildTileColorTable(const std::string& tileset_name) { int tile_x = (tile % tileset_width_) * Tile::SIZE; int tile_y = (tile / tileset_width_) * Tile::SIZE; - // Contar frecuencia de cada color en el tile (ignorar transparente=255 y negro=0) + // Contar frecuencia de cada color en el tile (ignorar el color transparente del tileset) + Uint8 transparent = tileset->getTransparentColor(); std::array freq{}; for (int y = 0; y < Tile::SIZE; ++y) { for (int x = 0; x < Tile::SIZE; ++x) { Uint8 pixel = tileset->getPixel(tile_x + x, tile_y + y); - if (pixel != 0 && pixel != 255) { + if (pixel != transparent) { freq[pixel]++; } } } - // Encontrar el color más frecuente - Uint8 best_color = 0; + // Encontrar el color más frecuente (transparent = tile vacío, no se pinta) + Uint8 best_color = transparent; int best_count = 0; - for (int c = 1; c < 256; ++c) { + for (int c = 0; c < 256; ++c) { + if (c == transparent) { continue; } if (freq[c] > best_count) { best_count = freq[c]; best_color = static_cast(c); @@ -182,7 +186,7 @@ auto MiniMap::getRoomMiniSurface(const std::string& room_name) -> std::shared_pt if (tile < 0 || tile >= static_cast(tile_colors_.size())) { continue; } Uint8 color = tile_colors_[tile]; - if (color != 0) { + if (color != tileset_transparent_) { surface->putPixel(x, y, color); } } @@ -267,35 +271,125 @@ void MiniMap::drawConnections() { } } -// Renderiza el minimapa centrado en la room actual +// Centra el viewport en una room +void MiniMap::centerOnRoom(const std::string& room_name) { + auto it = room_positions_.find(room_name); + if (it == room_positions_.end()) { return; } + const auto& pos = it->second.pos; + + float room_cx = static_cast(cellPixelX(pos.x) + CELL_W / 2); + float room_cy = static_cast(cellPixelY(pos.y) + CELL_H / 2); + view_x_ = static_cast(PlayArea::WIDTH) / 2.0F - room_cx; + view_y_ = static_cast(PlayArea::HEIGHT) / 2.0F - room_cy; +} + +// Devuelve el nombre de la room en una posición de pantalla, o vacío si no hay ninguna +auto MiniMap::roomAtScreen(float screen_x, float screen_y) -> std::string { + // Convertir coordenada de pantalla a coordenada dentro del minimapa + float map_x = screen_x - view_x_; + float map_y = screen_y - view_y_; + + for (const auto& [name, mini] : room_positions_) { + float rx = static_cast(cellPixelX(mini.pos.x)); + float ry = static_cast(cellPixelY(mini.pos.y)); + if (map_x >= rx && map_x < rx + CELL_W && map_y >= ry && map_y < ry + CELL_H) { + return name; + } + } + return ""; +} + +// Renderiza el minimapa void MiniMap::render(const std::string& current_room) { if (!map_surface_) { return; } auto game_surface = Screen::get()->getRendererSurface(); if (!game_surface) { return; } - // Centrar el minimapa en el play area, centrado en la room actual - auto it = room_positions_.find(current_room); - if (it == room_positions_.end()) { return; } - const auto& current_pos = it->second.pos; - - int room_cx = cellPixelX(current_pos.x) + CELL_W / 2; - int room_cy = cellPixelY(current_pos.y) + CELL_H / 2; - - int offset_x = PlayArea::WIDTH / 2 - room_cx; - int offset_y = PlayArea::HEIGHT / 2 - room_cy; - - // Renderizar la surface del minimapa con offset - SDL_FRect dst = {.x = static_cast(offset_x), .y = static_cast(offset_y), + // Renderizar la surface del minimapa con el viewport actual (alineado a pixel) + float vx = std::floor(view_x_); + float vy = std::floor(view_y_); + SDL_FRect dst = {.x = vx, .y = vy, .w = static_cast(map_width_ + SHADOW_OFFSET), .h = static_cast(map_height_ + SHADOW_OFFSET)}; map_surface_->render(nullptr, &dst); - // Highlight de la room actual (borde bright_white alrededor de la celda) - float cur_x = static_cast(offset_x + cellPixelX(current_pos.x)); - float cur_y = static_cast(offset_y + cellPixelY(current_pos.y)); - SDL_FRect highlight = {.x = cur_x - 1, .y = cur_y - 1, - .w = static_cast(CELL_W + 2), .h = static_cast(CELL_H + 2)}; - game_surface->drawRectBorder(&highlight, stringToColor("bright_white")); + // Highlight de la room actual (solo si está completamente visible en el play area) + auto it = room_positions_.find(current_room); + if (it != room_positions_.end()) { + float cur_x = vx + static_cast(cellPixelX(it->second.pos.x)) - 1; + float cur_y = vy + static_cast(cellPixelY(it->second.pos.y)) - 1; + float cur_w = static_cast(CELL_W + 2); + float cur_h = static_cast(CELL_H + 2); + if (cur_x >= 0 && cur_y >= 0 && cur_x + cur_w <= PlayArea::WIDTH && cur_y + cur_h <= PlayArea::HEIGHT) { + SDL_FRect highlight = {.x = cur_x, .y = cur_y, .w = cur_w, .h = cur_h}; + game_surface->drawRectBorder(&highlight, stringToColor("bright_white")); + } + } +} + +// Maneja eventos del minimapa (drag para explorar, click para navegar) +void MiniMap::handleEvent(const SDL_Event& event, const std::string& current_room) { + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) { + // Guardar posición inicial para detectar si es click o drag + float mouse_x = 0.0F; + float mouse_y = 0.0F; + SDL_GetMouseState(&mouse_x, &mouse_y); + float render_x = 0.0F; + float render_y = 0.0F; + SDL_RenderCoordinatesFromWindow(Screen::get()->getRenderer(), mouse_x, mouse_y, &render_x, &render_y); + SDL_FRect dst_rect = Screen::get()->getGameSurfaceDstRect(); + + dragging_ = true; + drag_start_x_ = render_x - dst_rect.x; + drag_start_y_ = render_y - dst_rect.y; + view_start_x_ = view_x_; + view_start_y_ = view_y_; + } + + if (event.type == SDL_EVENT_MOUSE_MOTION && dragging_) { + float mouse_x = 0.0F; + float mouse_y = 0.0F; + SDL_GetMouseState(&mouse_x, &mouse_y); + float render_x = 0.0F; + float render_y = 0.0F; + SDL_RenderCoordinatesFromWindow(Screen::get()->getRenderer(), mouse_x, mouse_y, &render_x, &render_y); + SDL_FRect dst_rect = Screen::get()->getGameSurfaceDstRect(); + + float game_x = render_x - dst_rect.x; + float game_y = render_y - dst_rect.y; + view_x_ = view_start_x_ + (game_x - drag_start_x_); + view_y_ = view_start_y_ + (game_y - drag_start_y_); + } + + if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) { + if (dragging_) { + // Comprobar si fue click (sin mover) o drag + float mouse_x = 0.0F; + float mouse_y = 0.0F; + SDL_GetMouseState(&mouse_x, &mouse_y); + float render_x = 0.0F; + float render_y = 0.0F; + SDL_RenderCoordinatesFromWindow(Screen::get()->getRenderer(), mouse_x, mouse_y, &render_x, &render_y); + SDL_FRect dst_rect = Screen::get()->getGameSurfaceDstRect(); + + float game_x = render_x - dst_rect.x; + float game_y = render_y - dst_rect.y; + + float dx = game_x - drag_start_x_; + float dy = game_y - drag_start_y_; + bool was_click = (dx * dx + dy * dy) < 4.0F; // Menos de 2px de movimiento = click + + if (was_click) { + // Click: navegar a la room bajo el cursor + std::string room = roomAtScreen(game_x, game_y); + if (!room.empty() && room != current_room && on_navigate) { + on_navigate(room); + } + } + + dragging_ = false; + } + } } #endif // _DEBUG diff --git a/source/game/editor/mini_map.hpp b/source/game/editor/mini_map.hpp index 3d6c9d0..2ac156e 100644 --- a/source/game/editor/mini_map.hpp +++ b/source/game/editor/mini_map.hpp @@ -4,6 +4,7 @@ #include +#include // Para function #include // Para shared_ptr #include // Para string #include // Para unordered_map @@ -25,9 +26,14 @@ class MiniMap { ~MiniMap() = default; void render(const std::string& current_room); - void rebuild(Uint8 bg_color, Uint8 conn_color); // Regenera la surface final + void handleEvent(const SDL_Event& event, const std::string& current_room); + void rebuild(Uint8 bg_color, Uint8 conn_color); + void centerOnRoom(const std::string& room_name); [[nodiscard]] auto isReady() const -> bool { return !room_positions_.empty(); } + // Callback al hacer click en una minihabitación (nombre del room) + std::function on_navigate; + private: // Posición de una room en el grid del minimapa struct GridPos { @@ -47,12 +53,14 @@ class MiniMap { void composeFinalSurface(); auto getRoomMiniSurface(const std::string& room_name) -> std::shared_ptr; void drawConnections(); + auto roomAtScreen(float screen_x, float screen_y) -> std::string; auto cellPixelX(int grid_x) const -> int { return PADDING + (grid_x - min_grid_x_) * (CELL_W + GAP); } auto cellPixelY(int grid_y) const -> int { return PADDING + (grid_y - min_grid_y_) * (CELL_H + GAP); } // Tabla de color predominante por tile index - std::vector tile_colors_; // tile_index → palette color index - int tileset_width_{0}; // Ancho del tileset en tiles + std::vector tile_colors_; // tile_index → palette color index + int tileset_width_{0}; // Ancho del tileset en tiles + Uint8 tileset_transparent_{16}; // Color transparente del tileset // Rooms renderizadas y posicionadas std::unordered_map room_positions_; @@ -66,6 +74,15 @@ class MiniMap { int min_grid_x_{0}; int min_grid_y_{0}; + // Viewport: offset de la surface del minimapa respecto al play area + float view_x_{0.0F}; // Offset X actual + float view_y_{0.0F}; // Offset Y actual + bool dragging_{false}; + float drag_start_x_{0.0F}; // Posición del ratón al inicio del drag + float drag_start_y_{0.0F}; + float view_start_x_{0.0F}; // Viewport al inicio del drag + float view_start_y_{0.0F}; + // Constantes static constexpr int ROOM_W = 32; // Ancho de una room en pixels del minimapa static constexpr int ROOM_H = 16; // Alto de una room en pixels del minimapa diff --git a/source/game/scenes/game.cpp b/source/game/scenes/game.cpp index 295d1f8..f77c1d8 100644 --- a/source/game/scenes/game.cpp +++ b/source/game/scenes/game.cpp @@ -450,6 +450,7 @@ void Game::renderPlaying() { // Si el editor está activo, delegar el renderizado de entidades y statusbar if (MapEditor::get()->isActive()) { MapEditor::get()->render(); + renderRoomName(); Screen::get()->render(); return; }