feat(editor): brush amb preview snap-to-grid i pintura per clic

This commit is contained in:
JailDesigner committed 2026-04-23 12:11:54 +02:00
1 parent a612a8255f
commit 05cdcfa660
3 files changed
+86 -11

No files matched your search

+55 -11
View File
@@ -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<int>(CANVAS_PT.x) - tilemap.getOriginX();
const int CY = static_cast<int>(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
+24
View File
@@ -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<std::size_t>(DY) * static_cast<std::size_t>(CW)) + static_cast<std::size_t>(DX)] = TILESET->getPaletteColor(IDX);
}
}
}
#endif
void Tilemap::render() const {
+7
View File
@@ -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: