feat(editor): Tilemap editable amb setTileAt/save (Debug-only)
This commit is contained in:
1 parent
6d8423764d
commit
219f8033eb
2 files changed
+92
-16
No files matched your search
+54
-16
@@ -13,6 +13,10 @@
|
||||
#include "core/resources/resource_helper.hpp"
|
||||
#include "external/fkyaml_node.hpp"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include "game/map/tilemap_writer.hpp"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
// Copia un tile (col, fila) del tileset al buffer ARGB a la posició de
|
||||
@@ -45,8 +49,8 @@ Tilemap::Tilemap(const std::string& yaml_path) {
|
||||
const auto* end = begin + bytes.size();
|
||||
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||
|
||||
const auto TILESET_PATH = yaml["tileset"].get_value<std::string>();
|
||||
const auto TILE_SIZE = yaml["tile_size"].get_value<int>();
|
||||
tileset_path_ = yaml["tileset"].get_value<std::string>();
|
||||
tile_size_ = yaml["tile_size"].get_value<int>();
|
||||
if (yaml.contains("origin")) {
|
||||
origin_x_ = yaml["origin"][0].get_value<int>();
|
||||
origin_y_ = yaml["origin"][1].get_value<int>();
|
||||
@@ -55,10 +59,9 @@ Tilemap::Tilemap(const std::string& yaml_path) {
|
||||
// Carreguem el tileset al cache (idempotent) i el pillem per accedir
|
||||
// als seus píxels i paleta. La paleta del tileset ve del GIF mateix.
|
||||
// Ens cal abans de llegir `data` per saber quants tiles per fila té.
|
||||
const auto& cache_name = TILESET_PATH;
|
||||
Resource::Cache::get().loadSurface(cache_name, TILESET_PATH);
|
||||
const auto TILESET = Resource::Cache::get().getSurface(cache_name);
|
||||
const int TILESET_COLS = TILESET->getWidth() / TILE_SIZE;
|
||||
Resource::Cache::get().loadSurface(tileset_path_, tileset_path_);
|
||||
const auto TILESET = Resource::Cache::get().getSurface(tileset_path_);
|
||||
tileset_cols_ = TILESET->getWidth() / tile_size_;
|
||||
|
||||
// `data` és una seqüència de seqüències d'enters: cada fila és una
|
||||
// llista d'índexs lineals al tileset (esq→dre, dalt→baix). Un valor
|
||||
@@ -67,30 +70,65 @@ Tilemap::Tilemap(const std::string& yaml_path) {
|
||||
if (!data.is_sequence() || data.empty()) {
|
||||
throw std::runtime_error("Tilemap: `data` buit o amb format incorrecte a " + yaml_path);
|
||||
}
|
||||
const int ROWS = static_cast<int>(data.size());
|
||||
const int COLS = static_cast<int>(data[0].size());
|
||||
width_px_ = COLS * TILE_SIZE;
|
||||
height_px_ = ROWS * TILE_SIZE;
|
||||
rows_ = static_cast<int>(data.size());
|
||||
cols_ = static_cast<int>(data[0].size());
|
||||
width_px_ = cols_ * tile_size_;
|
||||
height_px_ = rows_ * tile_size_;
|
||||
pixels_.assign(
|
||||
static_cast<std::size_t>(width_px_) * static_cast<std::size_t>(height_px_),
|
||||
0U);
|
||||
|
||||
for (int row = 0; row < ROWS; ++row) {
|
||||
#ifdef _DEBUG
|
||||
data_.assign(static_cast<std::size_t>(rows_), std::vector<int>(static_cast<std::size_t>(cols_), -1));
|
||||
#endif
|
||||
|
||||
for (int row = 0; row < rows_; ++row) {
|
||||
const auto& line = data[row];
|
||||
const int LINE_COLS = static_cast<int>(line.size());
|
||||
for (int col = 0; col < COLS && col < LINE_COLS; ++col) {
|
||||
for (int col = 0; col < cols_ && col < LINE_COLS; ++col) {
|
||||
const int INDEX = line[col].get_value<int>();
|
||||
#ifdef _DEBUG
|
||||
data_[static_cast<std::size_t>(row)][static_cast<std::size_t>(col)] = INDEX;
|
||||
#endif
|
||||
if (INDEX < 0) { continue; }
|
||||
const int SRC_COL = INDEX % TILESET_COLS;
|
||||
const int SRC_ROW = INDEX / TILESET_COLS;
|
||||
blitTile(*TILESET, TILE_SIZE, SRC_COL, SRC_ROW, pixels_, width_px_, col * TILE_SIZE, row * TILE_SIZE);
|
||||
const int SRC_COL = INDEX % tileset_cols_;
|
||||
const int SRC_ROW = INDEX / tileset_cols_;
|
||||
blitTile(*TILESET, tile_size_, SRC_COL, SRC_ROW, pixels_, width_px_, col * tile_size_, row * tile_size_);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Tilemap: " << COLS << "x" << ROWS << " tiles ("
|
||||
std::cout << "Tilemap: " << cols_ << "x" << rows_ << " tiles ("
|
||||
<< width_px_ << "x" << height_px_ << " px) des de " << yaml_path << '\n';
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
void Tilemap::setTileAt(int col, int row, int index) {
|
||||
if (col < 0 || col >= cols_ || row < 0 || row >= rows_) { return; }
|
||||
if (index < 0) { return; } // El mapa no admet cel·les buides (política de projecte).
|
||||
// No validem l'índex contra el total de tiles del tileset: la
|
||||
// invariant la garanteix el TilePicker, que només ofereix índexs en
|
||||
// rang.
|
||||
|
||||
data_[static_cast<std::size_t>(row)][static_cast<std::size_t>(col)] = index;
|
||||
|
||||
// Netegem el rectangle del tile al buffer de píxels i re-blitegem.
|
||||
const int DX = col * tile_size_;
|
||||
const int DY = row * tile_size_;
|
||||
for (int y = 0; y < tile_size_; ++y) {
|
||||
auto* dst = &pixels_[(static_cast<std::size_t>(DY + y) * static_cast<std::size_t>(width_px_)) + static_cast<std::size_t>(DX)];
|
||||
for (int x = 0; x < tile_size_; ++x) { dst[x] = 0U; }
|
||||
}
|
||||
const auto TILESET = Resource::Cache::get().getSurface(tileset_path_);
|
||||
const int SRC_COL = index % tileset_cols_;
|
||||
const int SRC_ROW = index / tileset_cols_;
|
||||
blitTile(*TILESET, tile_size_, SRC_COL, SRC_ROW, pixels_, width_px_, DX, DY);
|
||||
}
|
||||
|
||||
auto Tilemap::save(const std::string& yaml_path) const -> bool {
|
||||
return TilemapWriter::saveDataBlock(yaml_path, data_);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Tilemap::render() const {
|
||||
auto& screen = Screen::get();
|
||||
auto* canvas = screen.getCanvasArgb();
|
||||
|
||||
@@ -27,10 +27,48 @@ class Tilemap {
|
||||
[[nodiscard]] auto getOriginX() const -> int { return origin_x_; }
|
||||
[[nodiscard]] auto getOriginY() const -> int { return origin_y_; }
|
||||
|
||||
// Metadades per a consumidors que necessiten conèixer la geometria
|
||||
// lògica del tilemap (p.ex. l'editor per a la conversió px→cel·la).
|
||||
// Sempre disponibles (en Debug i Release) perquè són barates i no
|
||||
// lliguen amb cap codi específic de l'editor.
|
||||
[[nodiscard]] auto getTileSize() const -> int { return tile_size_; }
|
||||
[[nodiscard]] auto getCols() const -> int { return cols_; }
|
||||
[[nodiscard]] auto getRows() const -> int { return rows_; }
|
||||
[[nodiscard]] auto getTilesetPath() const -> const std::string& { return tileset_path_; }
|
||||
[[nodiscard]] auto getTilesetCols() const -> int { return tileset_cols_; }
|
||||
|
||||
#ifdef _DEBUG
|
||||
// API d'edició (només Debug). L'editor és absent en Release, així que
|
||||
// aquestes operacions no formen part de l'API pública del joc final.
|
||||
//
|
||||
// `setTileAt` valida els límits i reemplaça el tile de (col,row) per
|
||||
// `index`. Internament neteja el rectangle `tile_size×tile_size` del
|
||||
// buffer de píxels i re-blita el tile nou; el render al pròxim frame
|
||||
// ja reflectirà el canvi.
|
||||
//
|
||||
// `save` persisteix `data_` al YAML indicat via `TilemapWriter`, que
|
||||
// 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;
|
||||
#endif
|
||||
|
||||
private:
|
||||
int width_px_{0};
|
||||
int height_px_{0};
|
||||
int origin_x_{0};
|
||||
int origin_y_{0};
|
||||
int tile_size_{0};
|
||||
int cols_{0};
|
||||
int rows_{0};
|
||||
int tileset_cols_{0};
|
||||
std::string tileset_path_;
|
||||
std::vector<std::uint32_t> pixels_;
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Graella en memòria només necessària per a l'editor: guardar-la en
|
||||
// Release gastaria ~1 KB sense retorn, perquè el `pixels_` ja té tot
|
||||
// el que cal per renderitzar. En Debug la usem com a font de veritat
|
||||
// de l'edició i com a payload de `save()`.
|
||||
std::vector<std::vector<int>> data_;
|
||||
#endif
|
||||
};
|
||||
Reference in new issue
Block a user