#ifdef _DEBUG #include "game/editor/mini_map.hpp" #include // Para std::max, std::min #include // Para std::array #include // Para cout #include // Para std::map #include // Para queue (BFS) #include // Para set #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/resources/resource_cache.hpp" // Para Resource::Cache #include "game/gameplay/room.hpp" // Para Room::Data #include "utils/defines.hpp" // Para Tile::SIZE, PlayArea #include "utils/utils.hpp" // Para stringToColor // Constructor: construye todo el minimapa MiniMap::MiniMap() { buildTileColorTable("standard.gif"); layoutRooms(); buildRoomSurfaces(); composeFinalSurface(); } // Analiza el tileset y crea tabla: tile_index → color predominante void MiniMap::buildTileColorTable(const std::string& tileset_name) { auto tileset = Resource::Cache::get()->getSurface(tileset_name); if (!tileset) { return; } tileset_width_ = static_cast(tileset->getWidth()) / Tile::SIZE; int tileset_height = static_cast(tileset->getHeight()) / Tile::SIZE; int total_tiles = tileset_width_ * tileset_height; tile_colors_.resize(total_tiles, 0); for (int tile = 0; tile < total_tiles; ++tile) { 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) 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) { freq[pixel]++; } } } // Encontrar el color más frecuente Uint8 best_color = 0; int best_count = 0; for (int c = 1; c < 256; ++c) { if (freq[c] > best_count) { best_count = freq[c]; best_color = static_cast(c); } } tile_colors_[tile] = best_color; } } // Posiciona las rooms en un grid usando BFS desde las conexiones void MiniMap::layoutRooms() { auto& rooms = Resource::Cache::get()->getRooms(); if (rooms.empty()) { return; } // Mapa de nombre → Room::Data std::unordered_map> room_map; for (const auto& r : rooms) { room_map[r.name] = r.room; } // BFS para posicionar rooms std::set visited; std::queue> bfs; // Empezar por la primera room const std::string& start = rooms[0].name; bfs.push({start, {0, 0}}); visited.insert(start); // Grid ocupado: posición → nombre de room std::map, std::string> grid_occupied; while (!bfs.empty()) { auto [name, pos] = bfs.front(); bfs.pop(); auto key = std::make_pair(pos.x, pos.y); if (grid_occupied.contains(key)) { continue; } grid_occupied[key] = name; room_positions_[name] = RoomMini{.surface = nullptr, .pos = pos}; auto it = room_map.find(name); if (it == room_map.end()) { continue; } const auto& data = it->second; // Vecinos: up, down, left, right struct Neighbor { std::string room; int dx, dy; }; std::array neighbors = {{ {data->upper_room, 0, -1}, {data->lower_room, 0, 1}, {data->left_room, -1, 0}, {data->right_room, 1, 0}, }}; for (const auto& [neighbor_name, dx, dy] : neighbors) { if (neighbor_name == "0" || neighbor_name.empty()) { continue; } if (visited.contains(neighbor_name)) { continue; } GridPos neighbor_pos = {pos.x + dx, pos.y + dy}; auto nkey = std::make_pair(neighbor_pos.x, neighbor_pos.y); if (!grid_occupied.contains(nkey)) { visited.insert(neighbor_name); bfs.push({neighbor_name, neighbor_pos}); } } } // Calcular bounds del grid min_grid_x_ = 0; min_grid_y_ = 0; int max_grid_x = 0; int max_grid_y = 0; for (const auto& [name, mini] : room_positions_) { min_grid_x_ = std::min(min_grid_x_, mini.pos.x); min_grid_y_ = std::min(min_grid_y_, mini.pos.y); max_grid_x = std::max(max_grid_x, mini.pos.x); max_grid_y = std::max(max_grid_y, mini.pos.y); } int cols = max_grid_x - min_grid_x_ + 1; int rows = max_grid_y - min_grid_y_ + 1; map_width_ = cols * (CELL_W + GAP) - GAP + PADDING * 2; map_height_ = rows * (CELL_H + GAP) - GAP + PADDING * 2; std::cout << "MiniMap: " << room_positions_.size() << " rooms, grid " << cols << "x" << rows << " → " << map_width_ << "x" << map_height_ << " px\n"; } // Genera una mini-surface de 32x16 por room void MiniMap::buildRoomSurfaces() { for (auto& [name, mini] : room_positions_) { mini.surface = getRoomMiniSurface(name); } } // Genera la mini-surface de una room: 1 pixel por tile, color predominante auto MiniMap::getRoomMiniSurface(const std::string& room_name) -> std::shared_ptr { auto room_data = Resource::Cache::get()->getRoom(room_name); if (!room_data) { return nullptr; } auto surface = std::make_shared(ROOM_W, ROOM_H); auto prev = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(surface); surface->clear(stringToColor(room_data->bg_color)); const auto& tile_map = room_data->tile_map; for (int y = 0; y < ROOM_H; ++y) { for (int x = 0; x < ROOM_W; ++x) { int index = y * ROOM_W + x; if (index >= static_cast(tile_map.size())) { continue; } int tile = tile_map[index]; if (tile < 0 || tile >= static_cast(tile_colors_.size())) { continue; } Uint8 color = tile_colors_[tile]; if (color != 0) { surface->putPixel(x, y, color); } } } Screen::get()->setRendererSurface(prev); return surface; } // Compone la surface final con todas las rooms posicionadas void MiniMap::composeFinalSurface() { if (map_width_ <= 0 || map_height_ <= 0) { return; } // Surface un poco más grande para la sombra del borde inferior/derecho map_surface_ = std::make_shared(map_width_ + SHADOW_OFFSET, map_height_ + SHADOW_OFFSET); auto prev = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(map_surface_); // 1. Fondo general map_surface_->clear(COLOR_BACKGROUND); // 2. Líneas de conexión entre rooms (debajo de todo) drawConnections(); // 3. Sombras de las rooms (desplazadas 1px abajo-derecha) for (const auto& [name, mini] : room_positions_) { if (!mini.surface) { continue; } int px = cellPixelX(mini.pos.x) + SHADOW_OFFSET; int py = cellPixelY(mini.pos.y) + SHADOW_OFFSET; SDL_FRect shadow = {.x = static_cast(px), .y = static_cast(py), .w = static_cast(CELL_W), .h = static_cast(CELL_H)}; map_surface_->fillRect(&shadow, COLOR_SHADOW); } // 4. Borde negro de cada room + contenido de la room for (const auto& [name, mini] : room_positions_) { if (!mini.surface) { continue; } int px = cellPixelX(mini.pos.x); int py = cellPixelY(mini.pos.y); // Borde negro (la celda entera) SDL_FRect cell = {.x = static_cast(px), .y = static_cast(py), .w = static_cast(CELL_W), .h = static_cast(CELL_H)}; map_surface_->fillRect(&cell, COLOR_ROOM_BORDER); // Miniroom dentro del borde SDL_FRect dst = {.x = static_cast(px + BORDER), .y = static_cast(py + BORDER), .w = static_cast(ROOM_W), .h = static_cast(ROOM_H)}; mini.surface->render(nullptr, &dst); } Screen::get()->setRendererSurface(prev); } // Dibuja las líneas de conexión entre rooms vecinas void MiniMap::drawConnections() { for (const auto& [name, mini] : room_positions_) { auto room_data = Resource::Cache::get()->getRoom(name); if (!room_data) { continue; } int px = cellPixelX(mini.pos.x); int py = cellPixelY(mini.pos.y); // Conexión derecha: línea horizontal desde el borde derecho de esta room hasta el borde izquierdo de la vecina if (room_data->right_room != "0" && !room_data->right_room.empty() && room_positions_.contains(room_data->right_room)) { int x1 = px + CELL_W; int y_mid = py + CELL_H / 2; SDL_FRect line = {.x = static_cast(x1), .y = static_cast(y_mid), .w = static_cast(GAP), .h = 1.0F}; map_surface_->fillRect(&line, COLOR_CONNECTION); } // Conexión abajo: línea vertical desde el borde inferior de esta room hasta el borde superior de la vecina if (room_data->lower_room != "0" && !room_data->lower_room.empty() && room_positions_.contains(room_data->lower_room)) { int x_mid = px + CELL_W / 2; int y1 = py + CELL_H; SDL_FRect line = {.x = static_cast(x_mid), .y = static_cast(y1), .w = 1.0F, .h = static_cast(GAP)}; map_surface_->fillRect(&line, COLOR_CONNECTION); } } } // Renderiza el minimapa centrado en la room actual void MiniMap::render(const std::string& current_room) { if (!map_surface_) { return; } auto game_surface = Screen::get()->getRendererSurface(); if (!game_surface) { return; } // Fondo negro en el play area SDL_FRect bg = {.x = 0, .y = 0, .w = static_cast(PlayArea::WIDTH), .h = static_cast(PlayArea::HEIGHT)}; game_surface->fillRect(&bg, 0); // 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), .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")); } #endif // _DEBUG