#ifdef _DEBUG #include "game/editor/tile_picker.hpp" #include // Para std::clamp, std::min #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/resources/resource_cache.hpp" // Para Resource::Cache #include "utils/defines.hpp" // Para Tile::SIZE, PlayArea #include "utils/utils.hpp" // Para stringToColor // Margen del borde alrededor del tileset (en pixels) static constexpr int BORDER_PAD = 3; // Abre el picker con un tileset void TilePicker::open(const std::string& tileset_name, int current_tile, int bg_color, int source_color, int target_color, int tile_spacing_in, int tile_spacing_out) { tileset_ = Resource::Cache::get()->getSurface(tileset_name); if (!tileset_) { open_ = false; return; } spacing_in_ = tile_spacing_in; spacing_out_ = tile_spacing_out; // Calcular dimensiones del tileset en tiles (teniendo en cuenta spacing de entrada) int src_cell = Tile::SIZE + spacing_in_; tileset_width_ = static_cast(tileset_->getWidth()) / src_cell; tileset_height_ = static_cast(tileset_->getHeight()) / src_cell; // Corregir si el último tile cabe sin spacing if (tileset_width_ == 0 && tileset_->getWidth() >= Tile::SIZE) { tileset_width_ = 1; } if (tileset_height_ == 0 && tileset_->getHeight() >= Tile::SIZE) { tileset_height_ = 1; } current_tile_ = current_tile; hover_tile_ = -1; scroll_y_ = 0; // Dimensiones de salida (con spacing visual entre tiles) int out_cell = Tile::SIZE + spacing_out_; int display_w = tileset_width_ * out_cell - spacing_out_; // Sin trailing int display_h = tileset_height_ * out_cell - spacing_out_; // Frame: display + borde int frame_w = display_w + BORDER_PAD * 2; int frame_h = display_h + BORDER_PAD * 2; frame_surface_ = std::make_shared(frame_w, frame_h); // Componer: fondo + borde + tiles uno a uno { auto prev = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(frame_surface_); Uint8 fill_color = (bg_color >= 0) ? static_cast(bg_color) : stringToColor("black"); frame_surface_->clear(fill_color); // Borde doble SDL_FRect outer = {.x = 0, .y = 0, .w = static_cast(frame_w), .h = static_cast(frame_h)}; frame_surface_->drawRectBorder(&outer, stringToColor("bright_white")); SDL_FRect inner = {.x = 1, .y = 1, .w = static_cast(frame_w - 2), .h = static_cast(frame_h - 2)}; frame_surface_->drawRectBorder(&inner, stringToColor("white")); // Renderizar cada tile individualmente constexpr float TS = static_cast(Tile::SIZE); for (int row = 0; row < tileset_height_; ++row) { for (int col = 0; col < tileset_width_; ++col) { // Fuente: posición en el tileset original SDL_FRect src = { .x = static_cast(col * src_cell), .y = static_cast(row * src_cell), .w = TS, .h = TS}; // Destino: posición en el frame con spacing de salida int dst_x = BORDER_PAD + col * out_cell; int dst_y = BORDER_PAD + row * out_cell; if (source_color >= 0 && target_color >= 0) { tileset_->renderWithColorReplace(dst_x, dst_y, static_cast(source_color), static_cast(target_color), &src); } else { SDL_FRect dst = {.x = static_cast(dst_x), .y = static_cast(dst_y), .w = TS, .h = TS}; tileset_->render(&src, &dst); } } } Screen::get()->setRendererSurface(prev); } // Centrar en el play area offset_x_ = (PlayArea::WIDTH - frame_w) / 2; int offset_y = (PlayArea::HEIGHT - frame_h) / 2; if (offset_y < 0) { offset_y = 0; } visible_height_ = PlayArea::HEIGHT; frame_dst_ = {.x = static_cast(offset_x_), .y = static_cast(offset_y), .w = static_cast(frame_w), .h = static_cast(frame_h)}; // Si el frame es más alto que el play area, necesitará scroll if (frame_h > visible_height_) { frame_dst_.y = 0; if (current_tile_ >= 0) { int tile_row = current_tile_ / tileset_width_; int tile_y_px = tile_row * out_cell; if (tile_y_px > visible_height_ / 2) { scroll_y_ = tile_y_px - visible_height_ / 2; } } } open_ = true; } // Cierra el picker void TilePicker::close() { open_ = false; tileset_.reset(); frame_surface_.reset(); } // Renderiza el picker void TilePicker::render() { if (!open_ || !frame_surface_) { return; } auto game_surface = Screen::get()->getRendererSurface(); if (!game_surface) { return; } int frame_h = static_cast(frame_dst_.h); if (frame_h <= visible_height_) { frame_surface_->render(nullptr, &frame_dst_); } else { int max_scroll = frame_h - visible_height_; scroll_y_ = std::clamp(scroll_y_, 0, max_scroll); SDL_FRect src = {.x = 0, .y = static_cast(scroll_y_), .w = frame_dst_.w, .h = static_cast(visible_height_)}; SDL_FRect dst = {.x = frame_dst_.x, .y = 0, .w = frame_dst_.w, .h = static_cast(visible_height_)}; frame_surface_->render(&src, &dst); } // Highlights (en game_surface, encima del frame) int out_cell = Tile::SIZE + spacing_out_; float tileset_screen_x = frame_dst_.x + BORDER_PAD; float tileset_screen_y = frame_dst_.y + BORDER_PAD - static_cast(scroll_y_); constexpr float TS = static_cast(Tile::SIZE); // Highlight del tile bajo el cursor (blanco) if (hover_tile_ >= 0) { int col = hover_tile_ % tileset_width_; int row = hover_tile_ / tileset_width_; float hx = tileset_screen_x + static_cast(col * out_cell); float hy = tileset_screen_y + static_cast(row * out_cell); if (hy >= 0 && hy + TS <= visible_height_) { SDL_FRect highlight = {.x = hx, .y = hy, .w = TS, .h = TS}; game_surface->drawRectBorder(&highlight, stringToColor("bright_white")); } } // Highlight del tile actual (verde) if (current_tile_ >= 0) { int col = current_tile_ % tileset_width_; int row = current_tile_ / tileset_width_; float cx = tileset_screen_x + static_cast(col * out_cell); float cy = tileset_screen_y + static_cast(row * out_cell); if (cy >= 0 && cy + TS <= visible_height_) { SDL_FRect cur_rect = {.x = cx, .y = cy, .w = TS, .h = TS}; game_surface->drawRectBorder(&cur_rect, stringToColor("bright_green")); } } } // Maneja eventos del picker void TilePicker::handleEvent(const SDL_Event& event) { if (!open_) { return; } if (event.type == SDL_EVENT_MOUSE_MOTION) { updateMousePosition(); } if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { if (event.button.button == SDL_BUTTON_LEFT && hover_tile_ >= 0) { if (on_select) { on_select(hover_tile_); } close(); } else if (event.button.button == SDL_BUTTON_RIGHT) { close(); } } if (event.type == SDL_EVENT_MOUSE_WHEEL) { scroll_y_ -= static_cast(event.wheel.y) * Tile::SIZE * 2; int max_scroll = static_cast(frame_dst_.h) - visible_height_; if (max_scroll < 0) { max_scroll = 0; } scroll_y_ = std::clamp(scroll_y_, 0, max_scroll); updateMousePosition(); } if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE) { close(); } } // Calcula qué tile está bajo el cursor void TilePicker::updateMousePosition() { 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; // Coordenada relativa al contenido del frame (con scroll) float rel_x = game_x - frame_dst_.x - BORDER_PAD; float rel_y = game_y - frame_dst_.y - BORDER_PAD + static_cast(scroll_y_); // Convertir a tile teniendo en cuenta el spacing de salida int out_cell = Tile::SIZE + spacing_out_; int tile_x = static_cast(rel_x) / out_cell; int tile_y = static_cast(rel_y) / out_cell; // Verificar que estamos sobre un tile y no sobre el spacing int local_x = static_cast(rel_x) % out_cell; int local_y = static_cast(rel_y) % out_cell; bool on_tile = (local_x < Tile::SIZE && local_y < Tile::SIZE); if (on_tile && rel_x >= 0 && rel_y >= 0 && tile_x >= 0 && tile_x < tileset_width_ && tile_y >= 0 && tile_y < tileset_height_) { hover_tile_ = tile_y * tileset_width_ + tile_x; } else { hover_tile_ = -1; } } #endif // _DEBUG