Files
jaildoctors_dilemma/source/game/editor/tile_picker.cpp

209 lines
8.1 KiB
C++

#ifdef _DEBUG
#include "game/editor/tile_picker.hpp"
#include <algorithm> // 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) {
tileset_ = Resource::Cache::get()->getSurface(tileset_name);
if (!tileset_) {
open_ = false;
return;
}
tileset_width_ = static_cast<int>(tileset_->getWidth()) / Tile::SIZE;
tileset_height_ = static_cast<int>(tileset_->getHeight()) / Tile::SIZE;
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;
// 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_surface_ = std::make_shared<Surface>(frame_w, frame_h);
// Componer: fondo + borde + tileset (con sustitución de color opcional)
{
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<Uint8>(bg_color) : stringToColor("black");
frame_surface_->clear(fill_color);
// Borde doble (bright_white + white)
SDL_FRect outer = {.x = 0, .y = 0, .w = static_cast<float>(frame_w), .h = static_cast<float>(frame_h)};
frame_surface_->drawRectBorder(&outer, stringToColor("bright_white"));
SDL_FRect inner = {.x = 1, .y = 1, .w = static_cast<float>(frame_w - 2), .h = static_cast<float>(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<Uint8>(source_color), static_cast<Uint8>(target_color));
} else {
SDL_FRect dst_rect = {.x = static_cast<float>(BORDER_PAD), .y = static_cast<float>(BORDER_PAD), .w = static_cast<float>(tileset_px_w), .h = static_cast<float>(tileset_px_h)};
tileset_->render(nullptr, &dst_rect);
}
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; }
// Área visible
visible_height_ = PlayArea::HEIGHT;
// Posición de destino del frame
frame_dst_ = {.x = static_cast<float>(offset_x_), .y = static_cast<float>(offset_y), .w = static_cast<float>(frame_w), .h = static_cast<float>(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;
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<int>(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<float>(scroll_y_), .w = frame_dst_.w, .h = static_cast<float>(visible_height_)};
SDL_FRect dst = {.x = frame_dst_.x, .y = 0, .w = frame_dst_.w, .h = static_cast<float>(visible_height_)};
frame_surface_->render(&src, &dst);
}
// Los highlights se dibujan directamente en game_surface (encima del frame)
float tileset_screen_x = frame_dst_.x + BORDER_PAD;
float tileset_screen_y = frame_dst_.y + BORDER_PAD - static_cast<float>(scroll_y_);
// 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<float>(col * Tile::SIZE);
float hy = tileset_screen_y + static_cast<float>(row * Tile::SIZE);
if (hy >= 0 && hy + Tile::SIZE <= visible_height_) {
SDL_FRect highlight = {.x = hx, .y = hy, .w = static_cast<float>(Tile::SIZE), .h = static_cast<float>(Tile::SIZE)};
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<float>(col * Tile::SIZE);
float cy = tileset_screen_y + static_cast<float>(row * Tile::SIZE);
if (cy >= 0 && cy + Tile::SIZE <= visible_height_) {
SDL_FRect cur_rect = {.x = cx, .y = cy, .w = static_cast<float>(Tile::SIZE), .h = static_cast<float>(Tile::SIZE)};
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<int>(event.wheel.y) * Tile::SIZE * 2;
int max_scroll = static_cast<int>(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 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<float>(scroll_y_);
int tile_x = static_cast<int>(tileset_x) / Tile::SIZE;
int tile_y = static_cast<int>(tileset_y) / Tile::SIZE;
if (tileset_x >= 0 && tile_x >= 0 && tile_x < tileset_width_ &&
tileset_y >= 0 && tile_y >= 0 && tile_y < tileset_height_) {
hover_tile_ = tile_y * tileset_width_ + tile_x;
} else {
hover_tile_ = -1;
}
}
#endif // _DEBUG