diff --git a/source/game/editor/editor.cpp b/source/game/editor/editor.cpp index 8552a64..9a78b47 100644 --- a/source/game/editor/editor.cpp +++ b/source/game/editor/editor.cpp @@ -7,6 +7,7 @@ #include "core/locale/locale.hpp" #include "core/rendering/notification_presets.hpp" #include "core/rendering/screen.hpp" +#include "core/system/event_buffer.hpp" #include "game/map/tilemap.hpp" #include "utils/defaults.hpp" #include "utils/defines.hpp" @@ -35,25 +36,68 @@ void Editor::toggle(Tilemap& tilemap) { } } -// cppcheck-suppress constParameterReference -// Tilemap& mutable: al pas següent `setTileAt()` (no const) el muta. void Editor::update(int /*delta_ms*/, Tilemap& tilemap) { if (!active_) { return; } - cursor_col_ = -1; - cursor_row_ = -1; - (void)tilemap; // TODO: brush + click + picker al pas següent. + + // 1. Cursor → cel·la. Convertim la posició del ratolí (espai finestra) + // a coordenades del canvas amb Screen::windowToCanvas, i d'allí al + // sistema de cel·les del tilemap restant l'origin i dividint pel + // tile_size. Fora del mapa → cursor_col/row = -1. + float mouse_x = 0.0F; + float mouse_y = 0.0F; + SDL_GetMouseState(&mouse_x, &mouse_y); + const auto CANVAS_PT = Screen::get().windowToCanvas(mouse_x, mouse_y); + const int CX = static_cast(CANVAS_PT.x) - tilemap.getOriginX(); + const int CY = static_cast(CANVAS_PT.y) - tilemap.getOriginY(); + const int TS = tilemap.getTileSize(); + int col = -1; + int row = -1; + if (TS > 0 && CX >= 0 && CY >= 0) { + const int C = CX / TS; + const int R = CY / TS; + if (C < tilemap.getCols() && R < tilemap.getRows()) { + col = C; + row = R; + } + } + cursor_col_ = col; + cursor_row_ = row; + + // 2. Clicks: només per-esdeveniment (no per estat). Un clic = un + // tile col·locat o brush netejat. El picker encara no existeix, així + // que el right-click no obre res: només neteja el brush. + for (const auto& ev : EventBuffer::events) { + if (ev.type != SDL_EVENT_MOUSE_BUTTON_DOWN) { continue; } + if (ev.button.button == SDL_BUTTON_LEFT) { + if (brush_ >= 0 && cursor_col_ >= 0 && cursor_row_ >= 0) { + tilemap.setTileAt(cursor_col_, cursor_row_, brush_); + } + } else if (ev.button.button == SDL_BUTTON_RIGHT) { + brush_ = -1; + } + } } -void Editor::render(const Tilemap& /*tilemap*/) const { +void Editor::render(const Tilemap& tilemap) const { if (!active_) { return; } auto& screen = Screen::get(); constexpr int W = GameCanvas::WIDTH; constexpr int H = GameCanvas::HEIGHT; - constexpr auto ARGB = Defaults::Editor::BORDER_ARGB; - screen.fillRect(0, 0, W, 1, ARGB); // top - screen.fillRect(0, H - 1, W, 1, ARGB); // bottom - screen.fillRect(0, 0, 1, H, ARGB); // left - screen.fillRect(W - 1, 0, 1, H, ARGB); // right + constexpr auto BORDER = Defaults::Editor::BORDER_ARGB; + + // Preview del brush: blitejem el tile seleccionat a la cel·la sota + // el cursor, abans del border, així el border no el talla si caiguera + // al perímetre. Cap efecte si no hi ha brush o el cursor està fora. + if (brush_ >= 0 && cursor_col_ >= 0 && cursor_row_ >= 0) { + const int PX = tilemap.getOriginX() + (cursor_col_ * tilemap.getTileSize()); + const int PY = tilemap.getOriginY() + (cursor_row_ * tilemap.getTileSize()); + tilemap.renderTilePreview(PX, PY, brush_); + } + + screen.fillRect(0, 0, W, 1, BORDER); // top + screen.fillRect(0, H - 1, W, 1, BORDER); // bottom + screen.fillRect(0, 0, 1, H, BORDER); // left + screen.fillRect(W - 1, 0, 1, H, BORDER); // right } #endif // _DEBUG diff --git a/source/game/map/tilemap.cpp b/source/game/map/tilemap.cpp index 64cbc93..e9c69ec 100644 --- a/source/game/map/tilemap.cpp +++ b/source/game/map/tilemap.cpp @@ -127,6 +127,30 @@ void Tilemap::setTileAt(int col, int row, int index) { auto Tilemap::save(const std::string& yaml_path) const -> bool { return TilemapWriter::saveDataBlock(yaml_path, data_); } + +void Tilemap::renderTilePreview(int canvas_x, int canvas_y, int tile_index) const { + if (tile_index < 0) { return; } + const auto TILESET = Resource::Cache::get().getSurface(tileset_path_); + if (TILESET == nullptr) { return; } + auto& screen = Screen::get(); + auto* buf = screen.getCanvasArgb(); + const int CW = screen.getCanvasWidth(); + const int CH = screen.getCanvasHeight(); + const auto TRANSPARENT = TILESET->getTransparentColor(); + const int SX0 = (tile_index % tileset_cols_) * tile_size_; + const int SY0 = (tile_index / tileset_cols_) * tile_size_; + for (int y = 0; y < tile_size_; ++y) { + const int DY = canvas_y + y; + if (DY < 0 || DY >= CH) { continue; } + for (int x = 0; x < tile_size_; ++x) { + const int DX = canvas_x + x; + if (DX < 0 || DX >= CW) { continue; } + const auto IDX = TILESET->getPixel(SX0 + x, SY0 + y); + if (IDX == TRANSPARENT) { continue; } + buf[(static_cast(DY) * static_cast(CW)) + static_cast(DX)] = TILESET->getPaletteColor(IDX); + } + } +} #endif void Tilemap::render() const { diff --git a/source/game/map/tilemap.hpp b/source/game/map/tilemap.hpp index 61f9920..e5a6149 100644 --- a/source/game/map/tilemap.hpp +++ b/source/game/map/tilemap.hpp @@ -50,6 +50,13 @@ class Tilemap { // només toca el bloc `data:` i preserva la resta byte a byte. void setTileAt(int col, int row, int index); [[nodiscard]] auto save(const std::string& yaml_path) const -> bool; + + // Pinta un únic tile del tileset sobre el canvas del Screen a la + // posició (canvas_x, canvas_y). Els píxels transparents del tileset + // es respecten (deixen visible el que hi ha sota). Usat per l'editor + // com a preview del brush sota el cursor, sense tocar `pixels_` ni + // `data_` fins que l'usuari fa click. + void renderTilePreview(int canvas_x, int canvas_y, int tile_index) const; #endif private: