diff --git a/source/game/editor/tile_picker.cpp b/source/game/editor/tile_picker.cpp index 2a21d47..e8473ed 100644 --- a/source/game/editor/tile_picker.cpp +++ b/source/game/editor/tile_picker.cpp @@ -14,48 +14,77 @@ 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) { +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; } - tileset_width_ = static_cast(tileset_->getWidth()) / Tile::SIZE; - tileset_height_ = static_cast(tileset_->getHeight()) / Tile::SIZE; + 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; - int tileset_px_w = tileset_width_ * Tile::SIZE; - int tileset_px_h = tileset_height_ * Tile::SIZE; + // 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_; - // Crear surface con borde: tileset + BORDER_PAD pixels a cada lado - int frame_w = tileset_px_w + BORDER_PAD * 2; - int frame_h = tileset_px_h + BORDER_PAD * 2; + // 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 + tileset (con sustitución de color opcional) + // Componer: fondo + borde + tiles uno a uno { auto prev = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(frame_surface_); - // Fondo: color personalizado o negro por defecto Uint8 fill_color = (bg_color >= 0) ? static_cast(bg_color) : stringToColor("black"); frame_surface_->clear(fill_color); - // Borde doble (bright_white + white) + // 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")); - // Tileset dentro del borde (con o sin sustitución de color) - if (source_color >= 0 && target_color >= 0) { - tileset_->renderWithColorReplace(BORDER_PAD, BORDER_PAD, static_cast(source_color), static_cast(target_color)); - } else { - SDL_FRect dst_rect = {.x = static_cast(BORDER_PAD), .y = static_cast(BORDER_PAD), .w = static_cast(tileset_px_w), .h = static_cast(tileset_px_h)}; - tileset_->render(nullptr, &dst_rect); + // 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); @@ -66,19 +95,17 @@ void TilePicker::open(const std::string& tileset_name, int current_tile, int bg_ int offset_y = (PlayArea::HEIGHT - frame_h) / 2; if (offset_y < 0) { offset_y = 0; } - // Área visible visible_height_ = PlayArea::HEIGHT; - // Posición de destino del frame - frame_dst_ = {.x = static_cast(offset_x_), .y = static_cast(offset_y), .w = static_cast(frame_w), .h = static_cast(frame_h)}; + 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; - // Scroll hasta el tile actual si está fuera de vista if (current_tile_ >= 0) { int tile_row = current_tile_ / tileset_width_; - int tile_y_px = tile_row * Tile::SIZE; + int tile_y_px = tile_row * out_cell; if (tile_y_px > visible_height_ / 2) { scroll_y_ = tile_y_px - visible_height_ / 2; } @@ -105,30 +132,31 @@ void TilePicker::render() { int frame_h = static_cast(frame_dst_.h); if (frame_h <= visible_height_) { - // El frame cabe entero: renderizar tal cual (flotando sobre el mapa) frame_surface_->render(nullptr, &frame_dst_); } else { - // El frame no cabe: renderizar porción visible con scroll 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 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); } - // Los highlights se dibujan directamente en game_surface (encima del frame) + // 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 * Tile::SIZE); - float hy = tileset_screen_y + static_cast(row * Tile::SIZE); - if (hy >= 0 && hy + Tile::SIZE <= visible_height_) { - SDL_FRect highlight = {.x = hx, .y = hy, .w = static_cast(Tile::SIZE), .h = static_cast(Tile::SIZE)}; + 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")); } } @@ -137,10 +165,10 @@ void TilePicker::render() { if (current_tile_ >= 0) { int col = current_tile_ % tileset_width_; int row = current_tile_ / tileset_width_; - float cx = tileset_screen_x + static_cast(col * Tile::SIZE); - float cy = tileset_screen_y + static_cast(row * Tile::SIZE); - if (cy >= 0 && cy + Tile::SIZE <= visible_height_) { - SDL_FRect cur_rect = {.x = cx, .y = cy, .w = static_cast(Tile::SIZE), .h = static_cast(Tile::SIZE)}; + 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")); } } @@ -190,15 +218,23 @@ void TilePicker::updateMousePosition() { float game_x = render_x - dst_rect.x; float game_y = render_y - dst_rect.y; - // Coordenada relativa al tileset (dentro del frame, con scroll) - float tileset_x = game_x - frame_dst_.x - BORDER_PAD; - float tileset_y = game_y - frame_dst_.y - BORDER_PAD + static_cast(scroll_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_); - int tile_x = static_cast(tileset_x) / Tile::SIZE; - int tile_y = static_cast(tileset_y) / Tile::SIZE; + // 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; - if (tileset_x >= 0 && tile_x >= 0 && tile_x < tileset_width_ && - tileset_y >= 0 && tile_y >= 0 && tile_y < tileset_height_) { + // 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; diff --git a/source/game/editor/tile_picker.hpp b/source/game/editor/tile_picker.hpp index 62c35c4..e4f0ee4 100644 --- a/source/game/editor/tile_picker.hpp +++ b/source/game/editor/tile_picker.hpp @@ -26,8 +26,12 @@ class TilePicker { // Abre el picker con un tileset // bg_color: color de fondo del panel (-1 = negro) - // source_color/target_color: sustitución de color al renderizar el tileset (-1 = sin sustitución) - void open(const std::string& tileset_name, int current_tile = -1, int bg_color = -1, int source_color = -1, int target_color = -1); + // source_color/target_color: sustitución de color (-1 = sin sustitución) + // tile_spacing_in: pixels de separación entre tiles en el fichero fuente + // tile_spacing_out: pixels de separación visual entre tiles al mostrar + void open(const std::string& tileset_name, int current_tile = -1, + int bg_color = -1, int source_color = -1, int target_color = -1, + int tile_spacing_in = 0, int tile_spacing_out = 1); void close(); [[nodiscard]] auto isOpen() const -> bool { return open_; } @@ -49,6 +53,10 @@ class TilePicker { int current_tile_{-1}; // Tile actualmente seleccionado (highlight) int hover_tile_{-1}; // Tile bajo el cursor + // Spacing + int spacing_in_{0}; // Spacing en el fichero fuente + int spacing_out_{1}; // Spacing visual al mostrar + // Scroll y posicionamiento int scroll_y_{0}; // Scroll vertical en pixels int offset_x_{0}; // Offset X para centrar en pantalla