mes retocs a minimap. arreglat el calcul dels minipixels en els tiles amb transparent

This commit is contained in:
2026-04-02 19:49:43 +02:00
parent b5e822c65f
commit cccb2359cf
4 changed files with 160 additions and 38 deletions

View File

@@ -15,6 +15,7 @@
#include "core/resources/resource_cache.hpp" // Para Resource::Cache #include "core/resources/resource_cache.hpp" // Para Resource::Cache
#include "core/resources/resource_list.hpp" // Para Resource::List #include "core/resources/resource_list.hpp" // Para Resource::List
#include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar #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/editor/room_saver.hpp" // Para RoomSaver
#include "game/entities/player.hpp" // Para Player #include "game/entities/player.hpp" // Para Player
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager #include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
@@ -119,8 +120,19 @@ static auto parseColor(const std::string& value) -> Uint8 {
void MapEditor::toggleMiniMap() { void MapEditor::toggleMiniMap() {
if (!mini_map_) { if (!mini_map_) {
mini_map_ = std::make_unique<MiniMap>(parseColor(settings_.minimap_bg), parseColor(settings_.minimap_conn)); mini_map_ = std::make_unique<MiniMap>(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_; mini_map_visible_ = !mini_map_visible_;
if (mini_map_visible_) {
mini_map_->centerOnRoom(room_path_);
}
} }
auto MapEditor::setMiniMapBg(const std::string& color) -> std::string { auto MapEditor::setMiniMapBg(const std::string& color) -> std::string {
@@ -347,19 +359,17 @@ void MapEditor::handleEvent(const SDL_Event& event) {
return; return;
} }
// Si el mini mapa está visible, cerrarlo con click o ESC // Si el mini mapa está visible, delegar eventos (ESC o M para cerrar)
if (mini_map_visible_) { if (mini_map_visible_ && mini_map_) {
if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN || if (event.type == SDL_EVENT_KEY_DOWN &&
(event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE)) { (event.key.key == SDLK_ESCAPE || event.key.key == SDLK_M) &&
static_cast<int>(event.key.repeat) == 0) {
mini_map_visible_ = false; mini_map_visible_ = false;
return; return;
} }
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_M) { mini_map_->handleEvent(event, room_path_);
mini_map_visible_ = false;
return; return;
} }
return; // Bloquear otros eventos mientras el minimapa está visible
}
// ESC: desactivar brush // ESC: desactivar brush
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE && brush_tile_ != NO_BRUSH) { if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE && brush_tile_ != NO_BRUSH) {

View File

@@ -3,6 +3,7 @@
#include "game/editor/mini_map.hpp" #include "game/editor/mini_map.hpp"
#include <algorithm> // Para std::max, std::min #include <algorithm> // Para std::max, std::min
#include <cmath> // Para std::floor
#include <array> // Para std::array #include <array> // Para std::array
#include <iostream> // Para cout #include <iostream> // Para cout
#include <map> // Para std::map #include <map> // Para std::map
@@ -37,6 +38,7 @@ void MiniMap::buildTileColorTable(const std::string& tileset_name) {
if (!tileset) { return; } if (!tileset) { return; }
tileset_width_ = static_cast<int>(tileset->getWidth()) / Tile::SIZE; tileset_width_ = static_cast<int>(tileset->getWidth()) / Tile::SIZE;
tileset_transparent_ = tileset->getTransparentColor();
int tileset_height = static_cast<int>(tileset->getHeight()) / Tile::SIZE; int tileset_height = static_cast<int>(tileset->getHeight()) / Tile::SIZE;
int total_tiles = tileset_width_ * tileset_height; 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_x = (tile % tileset_width_) * Tile::SIZE;
int tile_y = (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<int, 256> freq{}; std::array<int, 256> freq{};
for (int y = 0; y < Tile::SIZE; ++y) { for (int y = 0; y < Tile::SIZE; ++y) {
for (int x = 0; x < Tile::SIZE; ++x) { for (int x = 0; x < Tile::SIZE; ++x) {
Uint8 pixel = tileset->getPixel(tile_x + x, tile_y + y); Uint8 pixel = tileset->getPixel(tile_x + x, tile_y + y);
if (pixel != 0 && pixel != 255) { if (pixel != transparent) {
freq[pixel]++; freq[pixel]++;
} }
} }
} }
// Encontrar el color más frecuente // Encontrar el color más frecuente (transparent = tile vacío, no se pinta)
Uint8 best_color = 0; Uint8 best_color = transparent;
int best_count = 0; 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) { if (freq[c] > best_count) {
best_count = freq[c]; best_count = freq[c];
best_color = static_cast<Uint8>(c); best_color = static_cast<Uint8>(c);
@@ -182,7 +186,7 @@ auto MiniMap::getRoomMiniSurface(const std::string& room_name) -> std::shared_pt
if (tile < 0 || tile >= static_cast<int>(tile_colors_.size())) { continue; } if (tile < 0 || tile >= static_cast<int>(tile_colors_.size())) { continue; }
Uint8 color = tile_colors_[tile]; Uint8 color = tile_colors_[tile];
if (color != 0) { if (color != tileset_transparent_) {
surface->putPixel(x, y, color); 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<float>(cellPixelX(pos.x) + CELL_W / 2);
float room_cy = static_cast<float>(cellPixelY(pos.y) + CELL_H / 2);
view_x_ = static_cast<float>(PlayArea::WIDTH) / 2.0F - room_cx;
view_y_ = static_cast<float>(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<float>(cellPixelX(mini.pos.x));
float ry = static_cast<float>(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) { void MiniMap::render(const std::string& current_room) {
if (!map_surface_) { return; } if (!map_surface_) { return; }
auto game_surface = Screen::get()->getRendererSurface(); auto game_surface = Screen::get()->getRendererSurface();
if (!game_surface) { return; } if (!game_surface) { return; }
// Centrar el minimapa en el play area, centrado en la room actual // Renderizar la surface del minimapa con el viewport actual (alineado a pixel)
auto it = room_positions_.find(current_room); float vx = std::floor(view_x_);
if (it == room_positions_.end()) { return; } float vy = std::floor(view_y_);
const auto& current_pos = it->second.pos; SDL_FRect dst = {.x = vx, .y = vy,
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<float>(offset_x), .y = static_cast<float>(offset_y),
.w = static_cast<float>(map_width_ + SHADOW_OFFSET), .h = static_cast<float>(map_height_ + SHADOW_OFFSET)}; .w = static_cast<float>(map_width_ + SHADOW_OFFSET), .h = static_cast<float>(map_height_ + SHADOW_OFFSET)};
map_surface_->render(nullptr, &dst); map_surface_->render(nullptr, &dst);
// Highlight de la room actual (borde bright_white alrededor de la celda) // Highlight de la room actual (solo si está completamente visible en el play area)
float cur_x = static_cast<float>(offset_x + cellPixelX(current_pos.x)); auto it = room_positions_.find(current_room);
float cur_y = static_cast<float>(offset_y + cellPixelY(current_pos.y)); if (it != room_positions_.end()) {
SDL_FRect highlight = {.x = cur_x - 1, .y = cur_y - 1, float cur_x = vx + static_cast<float>(cellPixelX(it->second.pos.x)) - 1;
.w = static_cast<float>(CELL_W + 2), .h = static_cast<float>(CELL_H + 2)}; float cur_y = vy + static_cast<float>(cellPixelY(it->second.pos.y)) - 1;
float cur_w = static_cast<float>(CELL_W + 2);
float cur_h = static_cast<float>(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")); 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 #endif // _DEBUG

View File

@@ -4,6 +4,7 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <functional> // Para function
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string #include <string> // Para string
#include <unordered_map> // Para unordered_map #include <unordered_map> // Para unordered_map
@@ -25,9 +26,14 @@ class MiniMap {
~MiniMap() = default; ~MiniMap() = default;
void render(const std::string& current_room); 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(); } [[nodiscard]] auto isReady() const -> bool { return !room_positions_.empty(); }
// Callback al hacer click en una minihabitación (nombre del room)
std::function<void(const std::string&)> on_navigate;
private: private:
// Posición de una room en el grid del minimapa // Posición de una room en el grid del minimapa
struct GridPos { struct GridPos {
@@ -47,12 +53,14 @@ class MiniMap {
void composeFinalSurface(); void composeFinalSurface();
auto getRoomMiniSurface(const std::string& room_name) -> std::shared_ptr<Surface>; auto getRoomMiniSurface(const std::string& room_name) -> std::shared_ptr<Surface>;
void drawConnections(); 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 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); } auto cellPixelY(int grid_y) const -> int { return PADDING + (grid_y - min_grid_y_) * (CELL_H + GAP); }
// Tabla de color predominante por tile index // Tabla de color predominante por tile index
std::vector<Uint8> tile_colors_; // tile_index → palette color index std::vector<Uint8> tile_colors_; // tile_index → palette color index
int tileset_width_{0}; // Ancho del tileset en tiles int tileset_width_{0}; // Ancho del tileset en tiles
Uint8 tileset_transparent_{16}; // Color transparente del tileset
// Rooms renderizadas y posicionadas // Rooms renderizadas y posicionadas
std::unordered_map<std::string, RoomMini> room_positions_; std::unordered_map<std::string, RoomMini> room_positions_;
@@ -66,6 +74,15 @@ class MiniMap {
int min_grid_x_{0}; int min_grid_x_{0};
int min_grid_y_{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 // Constantes
static constexpr int ROOM_W = 32; // Ancho de una room en pixels del minimapa 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 static constexpr int ROOM_H = 16; // Alto de una room en pixels del minimapa

View File

@@ -450,6 +450,7 @@ void Game::renderPlaying() {
// Si el editor está activo, delegar el renderizado de entidades y statusbar // Si el editor está activo, delegar el renderizado de entidades y statusbar
if (MapEditor::get()->isActive()) { if (MapEditor::get()->isActive()) {
MapEditor::get()->render(); MapEditor::get()->render();
renderRoomName();
Screen::get()->render(); Screen::get()->render();
return; return;
} }