feat(editor): brush amb preview snap-to-grid i pintura per clic
This commit is contained in:
1 parent
a612a8255f
commit
05cdcfa660
3 files changed
+86
-11
No files matched your search
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user