refactor(tilemap): índexs directes al YAML en lloc de llegenda ASCII
`data` passa a ser una llista de files d'enters (tile index lineal), sense la indirecció `legend:` + símbols. Desapareix el límit implícit de ~60 tiles distints i el grid segueix llegint-se com a matriu visual gràcies al padding de 3 dígits. `-1` marca cel·la buida. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4da05b3d76
commit
243ba06351
3 files changed
+52
-96
No files matched your search
+22
-40
@@ -3,13 +3,11 @@
|
|||||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||||
#
|
#
|
||||||
# Símbols del grid:
|
# Format del tilemap:
|
||||||
# W = paret (tile 99 del tileset, o (3,3) en 0-indexed)
|
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||||
# F = sòl (llista = un tile aleatori entre aquests per cada casella)
|
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||||
# . = cap tile (els sarcòfags seran actors, ací deixem transparent)
|
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||||
#
|
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||||
# Índexs lineals: els tiles van d'esquerra a dreta, de dalt a baix. Amb un
|
|
||||||
# tileset de 512×256 i tiles de 16×16 hi ha 32 tiles per fila.
|
|
||||||
|
|
||||||
tileset: data/tilesets/tomb.gif
|
tileset: data/tilesets/tomb.gif
|
||||||
tile_size: 16
|
tile_size: 16
|
||||||
@@ -31,38 +29,22 @@ geometry:
|
|||||||
back_wall_h: 24
|
back_wall_h: 24
|
||||||
side_wall_w: 24
|
side_wall_w: 24
|
||||||
|
|
||||||
legend:
|
data:
|
||||||
W: 292
|
- [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
X: 169
|
- [ 1,230,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,231, 1]
|
||||||
Y: 1
|
- [ 1,202,292,292,292,292,292,292,292,292,292,292,292,292,292,292,292,292,292,200, 1]
|
||||||
Z: 202
|
- [ 1,202, 32, 32, 2, 32, 32, 34, 34, 32, 32, 2, 32, 32, 32, 32, 34, 32, 32,200, 1]
|
||||||
T: 200
|
- [ 1,202, 32, 2, 2, 2, 32, 2, 2, 2, 32, 2, 2, 2, 34, 2, 2, 2, 32,200, 1]
|
||||||
R: 262
|
- [ 1,202, 32, 32, 32, 33, 33, 32, 32, 32, 32, 32, 2, 32, 32, 32, 34, 32, 32,200, 1]
|
||||||
G: 263
|
- [ 1,202, 32, 2, 2, 2, 32, 2, 2, 2, 2, 2, 2, 2, 32, 2, 2, 2, 32,200, 1]
|
||||||
H: 230
|
- [ 1,202, 32, 32, 33, 33, 32, 32, 32, 32, 32, 2, 32, 32, 34, 32, 32, 32, 32,200, 1]
|
||||||
I: 231
|
- [ 1,202, 32, 2, 2, 2, 32, 2, 2, 2, 32, 2, 2, 2, 32, 2, 2, 2, 32,200, 1]
|
||||||
J: 233
|
- [ 1,202, 32, 32, 32, 32, 33, 33, 32, 32, 32, 32, 2, 32, 32, 32, 32, 32, 32,200, 1]
|
||||||
A: 2
|
- [ 1,202, 32, 2, 2, 2, 32, 2, 2, 2, 32, 2, 2, 2, 32, 2, 2, 2, 32,200, 1]
|
||||||
B: 34
|
- [ 1,202, 32, 32, 32, 2, 34, 32, 32, 32, 32, 2, 32, 33, 32, 32, 34, 32, 32,200, 1]
|
||||||
C: 33
|
- [ 1,202, 32, 2, 2, 2, 32, 2, 2, 2, 32, 2, 2, 2, 32, 2, 2, 2, 32,200, 1]
|
||||||
D: 32
|
- [ 1,202, 32, 32, 34, 34, 32, 32, 2, 32, 32, 32, 34, 33, 32, 32, 32, 32, 32,200, 1]
|
||||||
|
- [ 1,262,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,263, 1]
|
||||||
data: |
|
|
||||||
YYYYYYYYYYYYYYYYYYYYY
|
|
||||||
YHJJJJJJJJJJJJJJJJJIY
|
|
||||||
YZWWWWWWWWWWWWWWWWWTY
|
|
||||||
YZDDADDBBDDADDDDBDDTY
|
|
||||||
YZDAAADAAADAAABAAADTY
|
|
||||||
YZDDDCCDDDDDADDDBDDTY
|
|
||||||
YZDAAADAAAAAAADAAADTY
|
|
||||||
YZDDCCDDDDDADDBDDDDTY
|
|
||||||
YZDAAADAAADAAADAAADTY
|
|
||||||
YZDDDDCCDDDDADDDDDDTY
|
|
||||||
YZDAAADAAADAAADAAADTY
|
|
||||||
YZDDDABDDDDADCDDBDDTY
|
|
||||||
YZDAAADAAADAAADAAADTY
|
|
||||||
YZDDBBDDADDDBCDDDDDTY
|
|
||||||
YRXXXXXXXXXXXXXXXXXGY
|
|
||||||
|
|
||||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||||
@@ -75,7 +57,7 @@ data: |
|
|||||||
#
|
#
|
||||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||||
# d'obertura (R/G). La momia al corredor central. L'antorxa al costat
|
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||||
# superior esquerre per verificar render d'una entitat decorativa.
|
# superior esquerre per verificar render d'una entitat decorativa.
|
||||||
entities:
|
entities:
|
||||||
- type: torch
|
- type: torch
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
# per a poder carregar Map sense errors, però el seu propòsit real són
|
# per a poder carregar Map sense errors, però el seu propòsit real són
|
||||||
# els spawns de sota: l'smoke verifica que EntityLoader::readSpawns els
|
# els spawns de sota: l'smoke verifica que EntityLoader::readSpawns els
|
||||||
# retorna correctament i que EntityManager pot instanciar-los i wire-ar-los.
|
# retorna correctament i que EntityManager pot instanciar-los i wire-ar-los.
|
||||||
|
#
|
||||||
|
# Format del tilemap: vegeu pyramid.yaml.
|
||||||
|
|
||||||
tileset: data/tilesets/tomb.gif
|
tileset: data/tilesets/tomb.gif
|
||||||
tile_size: 16
|
tile_size: 16
|
||||||
@@ -18,18 +20,14 @@ geometry:
|
|||||||
back_wall_h: 24
|
back_wall_h: 24
|
||||||
side_wall_w: 24
|
side_wall_w: 24
|
||||||
|
|
||||||
legend:
|
data:
|
||||||
Y: 1
|
- [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
X: 169
|
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||||
|
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||||
data: |
|
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||||
YYYYYYYYYYYYYY
|
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||||
YXXXXXXXXXXXXY
|
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||||
YXXXXXXXXXXXXY
|
- [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
YXXXXXXXXXXXXY
|
|
||||||
YXXXXXXXXXXXXY
|
|
||||||
YXXXXXXXXXXXXY
|
|
||||||
YYYYYYYYYYYYYY
|
|
||||||
|
|
||||||
entities:
|
entities:
|
||||||
- type: torch
|
- type: torch
|
||||||
|
|||||||
+20
-44
@@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <unordered_map>
|
#include <vector>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "core/rendering/screen.hpp"
|
#include "core/rendering/screen.hpp"
|
||||||
#include "core/rendering/surface.hpp"
|
#include "core/rendering/surface.hpp"
|
||||||
@@ -16,35 +15,6 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Cada símbol del grid es mapeja a (col, fila) dins del tileset.
|
|
||||||
using TileCoord = std::pair<int, int>;
|
|
||||||
using Legend = std::unordered_map<char, TileCoord>;
|
|
||||||
|
|
||||||
auto splitLines(const std::string& text) -> std::vector<std::string> {
|
|
||||||
std::vector<std::string> lines;
|
|
||||||
std::istringstream iss(text);
|
|
||||||
std::string line;
|
|
||||||
while (std::getline(iss, line)) {
|
|
||||||
if (!line.empty()) { lines.push_back(line); }
|
|
||||||
}
|
|
||||||
return lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
// La llegenda usa índexs lineals del tileset (esq→dre, dalt→baix).
|
|
||||||
// Els convertim a (col, fila) amb el nombre de tiles per fila del tileset.
|
|
||||||
auto parseLegend(const fkyaml::node& node, int tileset_cols) -> Legend {
|
|
||||||
Legend legend;
|
|
||||||
for (auto it = node.begin(); it != node.end(); ++it) {
|
|
||||||
const auto KEY_STR = it.key().get_value<std::string>();
|
|
||||||
if (KEY_STR.empty()) { continue; }
|
|
||||||
const auto INDEX = it.value().get_value<int>();
|
|
||||||
const int COL = INDEX % tileset_cols;
|
|
||||||
const int ROW = INDEX / tileset_cols;
|
|
||||||
legend[KEY_STR.front()] = {COL, ROW};
|
|
||||||
}
|
|
||||||
return legend;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copia un tile (col, fila) del tileset al buffer ARGB a la posició de
|
// Copia un tile (col, fila) del tileset al buffer ARGB a la posició de
|
||||||
// destí (dest_x, dest_y). Els píxels amb l'índex de transparència del
|
// destí (dest_x, dest_y). Els píxels amb l'índex de transparència del
|
||||||
// tileset es deixen com estan al buffer (alpha=0 per defecte).
|
// tileset es deixen com estan al buffer (alpha=0 per defecte).
|
||||||
@@ -84,17 +54,21 @@ Tilemap::Tilemap(const std::string& yaml_path) {
|
|||||||
|
|
||||||
// Carreguem el tileset al cache (idempotent) i el pillem per accedir
|
// 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.
|
// als seus píxels i paleta. La paleta del tileset ve del GIF mateix.
|
||||||
// Ens cal abans de la llegenda per saber quants tiles per fila té.
|
// Ens cal abans de llegir `data` per saber quants tiles per fila té.
|
||||||
const auto& cache_name = TILESET_PATH;
|
const auto& cache_name = TILESET_PATH;
|
||||||
Resource::Cache::get().loadSurface(cache_name, TILESET_PATH);
|
Resource::Cache::get().loadSurface(cache_name, TILESET_PATH);
|
||||||
const auto TILESET = Resource::Cache::get().getSurface(cache_name);
|
const auto TILESET = Resource::Cache::get().getSurface(cache_name);
|
||||||
const int TILESET_COLS = TILESET->getWidth() / TILE_SIZE;
|
const int TILESET_COLS = TILESET->getWidth() / TILE_SIZE;
|
||||||
|
|
||||||
const auto LEGEND = parseLegend(yaml["legend"], TILESET_COLS);
|
// `data` és una seqüència de seqüències d'enters: cada fila és una
|
||||||
const auto LINES = splitLines(yaml["data"].get_value<std::string>());
|
// llista d'índexs lineals al tileset (esq→dre, dalt→baix). Un valor
|
||||||
|
// negatiu (per convenció -1) deixa la cel·la buida (alpha=0).
|
||||||
const int ROWS = static_cast<int>(LINES.size());
|
const auto& data = yaml["data"];
|
||||||
const int COLS = ROWS > 0 ? static_cast<int>(LINES.front().size()) : 0;
|
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;
|
width_px_ = COLS * TILE_SIZE;
|
||||||
height_px_ = ROWS * TILE_SIZE;
|
height_px_ = ROWS * TILE_SIZE;
|
||||||
pixels_.assign(
|
pixels_.assign(
|
||||||
@@ -102,12 +76,14 @@ Tilemap::Tilemap(const std::string& yaml_path) {
|
|||||||
0U);
|
0U);
|
||||||
|
|
||||||
for (int row = 0; row < ROWS; ++row) {
|
for (int row = 0; row < ROWS; ++row) {
|
||||||
const auto& line = LINES[row];
|
const auto& line = data[row];
|
||||||
for (int col = 0; col < COLS && col < static_cast<int>(line.size()); ++col) {
|
const int LINE_COLS = static_cast<int>(line.size());
|
||||||
const char SYMBOL = line[col];
|
for (int col = 0; col < COLS && col < LINE_COLS; ++col) {
|
||||||
const auto IT = LEGEND.find(SYMBOL);
|
const int INDEX = line[col].get_value<int>();
|
||||||
if (IT == LEGEND.end()) { continue; }
|
if (INDEX < 0) { continue; }
|
||||||
blitTile(*TILESET, TILE_SIZE, IT->second.first, IT->second.second, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user