feat(editor): màquina d'estats Mode {TILE,ENTITY,HAND} + Picker
This commit is contained in:
1 parent
ca03a1307e
commit
2ef5968e0d
2 files changed
+196
-84
No files matched your search
+128
-63
@@ -111,6 +111,14 @@ namespace {
|
|||||||
void Editor::enter() {
|
void Editor::enter() {
|
||||||
if (active_) { return; }
|
if (active_) { return; }
|
||||||
active_ = true;
|
active_ = true;
|
||||||
|
// Estat de novella: tornem a Mode::TILE sense brush ni picker. Els
|
||||||
|
// brushs (tile/entity) i la selecció de la mà no es preserven entre
|
||||||
|
// sessions del editor: cada entrada parteix net.
|
||||||
|
mode_ = Mode::TILE;
|
||||||
|
picker_ = Picker::NONE;
|
||||||
|
brush_tile_ = -1;
|
||||||
|
brush_entity_.clear();
|
||||||
|
hand_selected_idx_ = -1;
|
||||||
Notification::notify(Locale::get().text("input.editor_on"));
|
Notification::notify(Locale::get().text("input.editor_on"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +128,7 @@ void Editor::enter() {
|
|||||||
void Editor::exit(Tilemap& tilemap) {
|
void Editor::exit(Tilemap& tilemap) {
|
||||||
if (!active_) { return; }
|
if (!active_) { return; }
|
||||||
active_ = false;
|
active_ = false;
|
||||||
picker_open_ = false;
|
picker_ = Picker::NONE;
|
||||||
// El writer salva sempre sobre la sala actualment carregada (segons
|
// El writer salva sempre sobre la sala actualment carregada (segons
|
||||||
// el LevelManager): així el "9" de l'editor desa el que tens davant,
|
// el LevelManager): així el "9" de l'editor desa el que tens davant,
|
||||||
// no una constant fixa. Resolem la ruta via Resource::Helper perquè
|
// no una constant fixa. Resolem la ruta via Resource::Helper perquè
|
||||||
@@ -140,9 +148,54 @@ void Editor::toggle(Tilemap& tilemap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Editor::applyHotkey(int scancode) -> bool {
|
||||||
|
const SDL_Scancode TILE_PICKER_SC = KeyConfig::get().scancode("editor_tile_picker");
|
||||||
|
const SDL_Scancode ENTITY_PICKER_SC = KeyConfig::get().scancode("editor_entity_picker");
|
||||||
|
const SDL_Scancode HAND_SC = KeyConfig::get().scancode("editor_hand");
|
||||||
|
|
||||||
|
if (TILE_PICKER_SC != SDL_SCANCODE_UNKNOWN && scancode == TILE_PICKER_SC) {
|
||||||
|
picker_ = (picker_ == Picker::TILE) ? Picker::NONE : Picker::TILE;
|
||||||
|
if (picker_ == Picker::TILE) { tile_picker_scroll_ = 0; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ENTITY_PICKER_SC != SDL_SCANCODE_UNKNOWN && scancode == ENTITY_PICKER_SC) {
|
||||||
|
picker_ = (picker_ == Picker::ENTITY) ? Picker::NONE : Picker::ENTITY;
|
||||||
|
if (picker_ == Picker::ENTITY) { entity_picker_scroll_ = 0; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (HAND_SC != SDL_SCANCODE_UNKNOWN && scancode == HAND_SC) {
|
||||||
|
mode_ = Mode::HAND;
|
||||||
|
picker_ = Picker::NONE;
|
||||||
|
hand_selected_idx_ = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// ESC només tanca el picker; fora d'ell, deixem que GlobalInputs el
|
||||||
|
// tracte per a la seva acció de confirmar-eixida.
|
||||||
|
if (picker_ != Picker::NONE && scancode == SDL_SCANCODE_ESCAPE) {
|
||||||
|
picker_ = Picker::NONE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::handleHotkeys() {
|
||||||
|
// Consumim les pròpies abans que cap altre handler (GlobalInputs) les
|
||||||
|
// veja. La transició concreta de cada tecla viu a `applyHotkey`.
|
||||||
|
for (auto it = EventBuffer::events.begin(); it != EventBuffer::events.end();) {
|
||||||
|
const bool IS_KEY = (it->type == SDL_EVENT_KEY_DOWN);
|
||||||
|
if (IS_KEY && applyHotkey(it->key.scancode)) {
|
||||||
|
it = EventBuffer::events.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Editor::update(int /*delta_ms*/, Tilemap& tilemap) {
|
void Editor::update(int /*delta_ms*/, Tilemap& tilemap) {
|
||||||
if (!active_) { return; }
|
if (!active_) { return; }
|
||||||
|
|
||||||
|
handleHotkeys();
|
||||||
|
|
||||||
// Posició del ratolí en coordenades del canvas (independent del
|
// Posició del ratolí en coordenades del canvas (independent del
|
||||||
// zoom de finestra i del mode d'escalat).
|
// zoom de finestra i del mode d'escalat).
|
||||||
float mouse_x = 0.0F;
|
float mouse_x = 0.0F;
|
||||||
@@ -152,43 +205,10 @@ void Editor::update(int /*delta_ms*/, Tilemap& tilemap) {
|
|||||||
const int CX = static_cast<int>(CP.x);
|
const int CX = static_cast<int>(CP.x);
|
||||||
const int CY = static_cast<int>(CP.y);
|
const int CY = static_cast<int>(CP.y);
|
||||||
|
|
||||||
// Consumim tecles pròpies del picker abans que cap altre handler
|
// Cursor → cel·la del tilemap. Comú a tots els modes amb mapa visible.
|
||||||
// (GlobalInputs) les vegi. ESC només es consumeix si el picker està
|
|
||||||
// obert — fora d'ell segueix arribant a GlobalInputs per la seva
|
|
||||||
// acció de confirmar-eixida.
|
|
||||||
const SDL_Scancode PICKER_SC = KeyConfig::get().scancode("editor_tile_picker");
|
|
||||||
for (auto it = EventBuffer::events.begin(); it != EventBuffer::events.end();) {
|
|
||||||
bool consumed = false;
|
|
||||||
if (it->type == SDL_EVENT_KEY_DOWN) {
|
|
||||||
if (PICKER_SC != SDL_SCANCODE_UNKNOWN && it->key.scancode == PICKER_SC) {
|
|
||||||
picker_open_ = !picker_open_;
|
|
||||||
if (picker_open_) { picker_scroll_ = 0; }
|
|
||||||
consumed = true;
|
|
||||||
} else if (picker_open_ && it->key.scancode == SDL_SCANCODE_ESCAPE) {
|
|
||||||
picker_open_ = false;
|
|
||||||
consumed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (consumed) {
|
|
||||||
it = EventBuffer::events.erase(it);
|
|
||||||
} else {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (picker_open_) {
|
|
||||||
updatePicker(tilemap, CX, CY);
|
|
||||||
} else {
|
|
||||||
updateMap(tilemap, CX, CY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Editor::updateMap(Tilemap& tilemap, int canvas_x, int canvas_y) {
|
|
||||||
// Cursor → cel·la del tilemap. Fora del mapa deixem -1/-1 perquè
|
|
||||||
// `render()` sàpiga no dibuixar preview i `setTileAt` ignori el clic.
|
|
||||||
const int TS = tilemap.getTileSize();
|
const int TS = tilemap.getTileSize();
|
||||||
const int MX = canvas_x - tilemap.getOriginX();
|
const int MX = CX - tilemap.getOriginX();
|
||||||
const int MY = canvas_y - tilemap.getOriginY();
|
const int MY = CY - tilemap.getOriginY();
|
||||||
cursor_col_ = -1;
|
cursor_col_ = -1;
|
||||||
cursor_row_ = -1;
|
cursor_row_ = -1;
|
||||||
if (TS > 0 && MX >= 0 && MY >= 0) {
|
if (TS > 0 && MX >= 0 && MY >= 0) {
|
||||||
@@ -200,21 +220,63 @@ void Editor::updateMap(Tilemap& tilemap, int canvas_x, int canvas_y) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& ev : EventBuffer::events) {
|
// Dispatch: el picker, si està obert, té prioritat sobre el mode.
|
||||||
if (ev.type != SDL_EVENT_MOUSE_BUTTON_DOWN) { continue; }
|
if (picker_ == Picker::TILE) {
|
||||||
if (ev.button.button == SDL_BUTTON_LEFT) {
|
updateTilePicker(tilemap, CX, CY);
|
||||||
if (brush_ >= 0 && cursor_col_ >= 0 && cursor_row_ >= 0) {
|
} else if (picker_ == Picker::ENTITY) {
|
||||||
tilemap.setTileAt(cursor_col_, cursor_row_, brush_);
|
updateEntityPicker(CX, CY);
|
||||||
}
|
} else {
|
||||||
} else if (ev.button.button == SDL_BUTTON_RIGHT) {
|
switch (mode_) {
|
||||||
brush_ = -1;
|
case Mode::TILE:
|
||||||
|
updateTileBrush(tilemap, CX, CY);
|
||||||
|
break;
|
||||||
|
case Mode::ENTITY:
|
||||||
|
updateEntityBrush(CX, CY);
|
||||||
|
break;
|
||||||
|
case Mode::HAND:
|
||||||
|
updateHand(CX, CY);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::updatePicker(const Tilemap& tilemap, int canvas_x, int canvas_y) {
|
void Editor::updateTileBrush(Tilemap& tilemap, int /*canvas_x*/, int /*canvas_y*/) {
|
||||||
|
for (const auto& ev : EventBuffer::events) {
|
||||||
|
if (ev.type != SDL_EVENT_MOUSE_BUTTON_DOWN) { continue; }
|
||||||
|
if (ev.button.button == SDL_BUTTON_LEFT) {
|
||||||
|
if (brush_tile_ >= 0 && cursor_col_ >= 0 && cursor_row_ >= 0) {
|
||||||
|
tilemap.setTileAt(cursor_col_, cursor_row_, brush_tile_);
|
||||||
|
}
|
||||||
|
} else if (ev.button.button == SDL_BUTTON_RIGHT) {
|
||||||
|
brush_tile_ = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTBEGIN(readability-convert-member-functions-to-static)
|
||||||
|
// Stubs: l'estat i la lògica concreta es construeixen als passos següents
|
||||||
|
// (entity placement, hand mode). Mantenim la signatura per estabilitat
|
||||||
|
// d'API i perquè update() pugui despatxar sense branques especials.
|
||||||
|
void Editor::updateEntityBrush(int /*canvas_x*/, int /*canvas_y*/) {
|
||||||
|
// Pas 5: col·locació amb snap per tipus + bloqueig de solapaments.
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::updateHand(int /*canvas_x*/, int /*canvas_y*/) {
|
||||||
|
// Pas 6: hover, select, move, delete, marc d'entitat seleccionada.
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::updateEntityPicker(int /*canvas_x*/, int /*canvas_y*/) {
|
||||||
|
// Pas 4: rejilla d'entitats registrades + click → brush_entity_.
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::renderEntityPicker(const Tilemap& /*tilemap*/) const {
|
||||||
|
// Pas 4: render del picker d'entitats.
|
||||||
|
}
|
||||||
|
// NOLINTEND(readability-convert-member-functions-to-static)
|
||||||
|
|
||||||
|
void Editor::updateTilePicker(const Tilemap& tilemap, int canvas_x, int canvas_y) {
|
||||||
const auto LAY = layoutPicker(tilemap);
|
const auto LAY = layoutPicker(tilemap);
|
||||||
picker_hover_ = -1;
|
tile_picker_hover_ = -1;
|
||||||
// Espai útil per als tiles dins de la caixa (descomptat el border 1 px).
|
// Espai útil per als tiles dins de la caixa (descomptat el border 1 px).
|
||||||
const int INNER_X = LAY.x + PICKER_BORDER;
|
const int INNER_X = LAY.x + PICKER_BORDER;
|
||||||
const int INNER_Y = LAY.y + PICKER_BORDER;
|
const int INNER_Y = LAY.y + PICKER_BORDER;
|
||||||
@@ -227,19 +289,20 @@ void Editor::updatePicker(const Tilemap& tilemap, int canvas_x, int canvas_y) {
|
|||||||
const bool IN_SPACING_X = (RX % LAY.pitch) >= LAY.tile_size;
|
const bool IN_SPACING_X = (RX % LAY.pitch) >= LAY.tile_size;
|
||||||
const bool IN_SPACING_Y = (RY % LAY.pitch) >= LAY.tile_size;
|
const bool IN_SPACING_Y = (RY % LAY.pitch) >= LAY.tile_size;
|
||||||
if (!IN_SPACING_X && !IN_SPACING_Y && VC < LAY.cols && VR < LAY.rows_visible) {
|
if (!IN_SPACING_X && !IN_SPACING_Y && VC < LAY.cols && VR < LAY.rows_visible) {
|
||||||
const int IDX = ((picker_scroll_ + VR) * LAY.cols) + VC;
|
const int IDX = ((tile_picker_scroll_ + VR) * LAY.cols) + VC;
|
||||||
if (IDX < LAY.total_tiles) { picker_hover_ = IDX; }
|
if (IDX < LAY.total_tiles) { tile_picker_hover_ = IDX; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& ev : EventBuffer::events) {
|
for (const auto& ev : EventBuffer::events) {
|
||||||
if (ev.type == SDL_EVENT_MOUSE_WHEEL) {
|
if (ev.type == SDL_EVENT_MOUSE_WHEEL) {
|
||||||
picker_scroll_ -= static_cast<int>(ev.wheel.y);
|
tile_picker_scroll_ -= static_cast<int>(ev.wheel.y);
|
||||||
picker_scroll_ = std::clamp(picker_scroll_, 0, LAY.max_scroll);
|
tile_picker_scroll_ = std::clamp(tile_picker_scroll_, 0, LAY.max_scroll);
|
||||||
} else if (ev.type == SDL_EVENT_MOUSE_BUTTON_DOWN && ev.button.button == SDL_BUTTON_LEFT) {
|
} else if (ev.type == SDL_EVENT_MOUSE_BUTTON_DOWN && ev.button.button == SDL_BUTTON_LEFT) {
|
||||||
if (picker_hover_ >= 0) {
|
if (tile_picker_hover_ >= 0) {
|
||||||
brush_ = picker_hover_;
|
brush_tile_ = tile_picker_hover_;
|
||||||
picker_open_ = false;
|
mode_ = Mode::TILE;
|
||||||
|
picker_ = Picker::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,13 +314,15 @@ void Editor::render(const Tilemap& tilemap) const {
|
|||||||
constexpr int W = GameCanvas::WIDTH;
|
constexpr int W = GameCanvas::WIDTH;
|
||||||
constexpr int H = GameCanvas::HEIGHT;
|
constexpr int H = GameCanvas::HEIGHT;
|
||||||
|
|
||||||
if (picker_open_) {
|
if (picker_ == Picker::TILE) {
|
||||||
renderPicker(tilemap);
|
renderTilePicker(tilemap);
|
||||||
} else if (brush_ >= 0 && cursor_col_ >= 0 && cursor_row_ >= 0) {
|
} else if (picker_ == Picker::ENTITY) {
|
||||||
|
renderEntityPicker(tilemap);
|
||||||
|
} else if (mode_ == Mode::TILE && brush_tile_ >= 0 && cursor_col_ >= 0 && cursor_row_ >= 0) {
|
||||||
// Preview del brush sota el cursor (només quan el picker és tancat).
|
// Preview del brush sota el cursor (només quan el picker és tancat).
|
||||||
const int PX = tilemap.getOriginX() + (cursor_col_ * tilemap.getTileSize());
|
const int PX = tilemap.getOriginX() + (cursor_col_ * tilemap.getTileSize());
|
||||||
const int PY = tilemap.getOriginY() + (cursor_row_ * tilemap.getTileSize());
|
const int PY = tilemap.getOriginY() + (cursor_row_ * tilemap.getTileSize());
|
||||||
tilemap.renderTilePreview(PX, PY, brush_);
|
tilemap.renderTilePreview(PX, PY, brush_tile_);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto BORDER = EditorConfig::get().borderArgb();
|
const auto BORDER = EditorConfig::get().borderArgb();
|
||||||
@@ -267,7 +332,7 @@ void Editor::render(const Tilemap& tilemap) const {
|
|||||||
screen.fillRect(W - 1, 0, 1, H, BORDER);
|
screen.fillRect(W - 1, 0, 1, H, BORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::renderPicker(const Tilemap& tilemap) const {
|
void Editor::renderTilePicker(const Tilemap& tilemap) const {
|
||||||
const auto LAY = layoutPicker(tilemap);
|
const auto LAY = layoutPicker(tilemap);
|
||||||
if (LAY.cols <= 0 || LAY.rows_visible <= 0) { return; }
|
if (LAY.cols <= 0 || LAY.rows_visible <= 0) { return; }
|
||||||
auto& screen = Screen::get();
|
auto& screen = Screen::get();
|
||||||
@@ -275,7 +340,7 @@ void Editor::renderPicker(const Tilemap& tilemap) const {
|
|||||||
screen.fillRect(LAY.x, LAY.y, LAY.w, LAY.h, cfg.pickerBgArgb());
|
screen.fillRect(LAY.x, LAY.y, LAY.w, LAY.h, cfg.pickerBgArgb());
|
||||||
|
|
||||||
for (int vr = 0; vr < LAY.rows_visible; ++vr) {
|
for (int vr = 0; vr < LAY.rows_visible; ++vr) {
|
||||||
const int LOGICAL_ROW = picker_scroll_ + vr;
|
const int LOGICAL_ROW = tile_picker_scroll_ + vr;
|
||||||
for (int vc = 0; vc < LAY.cols; ++vc) {
|
for (int vc = 0; vc < LAY.cols; ++vc) {
|
||||||
const int IDX = (LOGICAL_ROW * LAY.cols) + vc;
|
const int IDX = (LOGICAL_ROW * LAY.cols) + vc;
|
||||||
if (IDX >= LAY.total_tiles) { break; }
|
if (IDX >= LAY.total_tiles) { break; }
|
||||||
@@ -284,9 +349,9 @@ void Editor::renderPicker(const Tilemap& tilemap) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (picker_hover_ >= 0) {
|
if (tile_picker_hover_ >= 0) {
|
||||||
const int VR = (picker_hover_ / LAY.cols) - picker_scroll_;
|
const int VR = (tile_picker_hover_ / LAY.cols) - tile_picker_scroll_;
|
||||||
const int VC = picker_hover_ % LAY.cols;
|
const int VC = tile_picker_hover_ % LAY.cols;
|
||||||
if (VR >= 0 && VR < LAY.rows_visible) {
|
if (VR >= 0 && VR < LAY.rows_visible) {
|
||||||
const auto [PX, PY] = pickerTileOrigin(LAY, VC, VR);
|
const auto [PX, PY] = pickerTileOrigin(LAY, VC, VR);
|
||||||
const int TS = LAY.tile_size;
|
const int TS = LAY.tile_size;
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class Tilemap;
|
class Tilemap;
|
||||||
|
|
||||||
// Mode editor modal del GameScene. Només disponible en Debug — en Release
|
// Mode editor modal del GameScene. Només disponible en Debug — en Release
|
||||||
@@ -11,13 +14,21 @@ class Tilemap;
|
|||||||
// L'editor NO gestiona la tecla de toggle pròpia (`editor_toggle`): això
|
// L'editor NO gestiona la tecla de toggle pròpia (`editor_toggle`): això
|
||||||
// ho fa GameScene per poder decidir la coexistència amb la pausa F11
|
// ho fa GameScene per poder decidir la coexistència amb la pausa F11
|
||||||
// (mútuament excloents). L'editor s'encarrega de:
|
// (mútuament excloents). L'editor s'encarrega de:
|
||||||
// - Estat propi (actiu/no actiu, brush, picker obert).
|
// - Estat propi (mode actiu, brush per mode, picker obert).
|
||||||
// - Input del ratolí i tecles internes (`T`, `ESC`).
|
// - Input del ratolí i tecles internes (`T`, `E`, `H`, `Supr`, `ESC`).
|
||||||
// - Render del border blau + brush preview + picker.
|
// - Render del border blau + brush preview + picker.
|
||||||
// - Persistència del tilemap al YAML del repo en sortir.
|
// - Persistència del tilemap i de les entitats al YAML del repo en sortir.
|
||||||
//
|
//
|
||||||
// GameScene, quan `active()` és true, salta el tick de jugadors/entitats
|
// Modes del cursor (`Mode`):
|
||||||
// i no els pinta; el tilemap sí es pinta perquè és el que s'està editant.
|
// - TILE : el clic esquerre pinta el `brush_tile_` al tilemap.
|
||||||
|
// - ENTITY : el clic esquerre col·loca una instància de `brush_entity_`.
|
||||||
|
// - HAND : selecciona/mou/borra entitats existents.
|
||||||
|
//
|
||||||
|
// Pickers (`Picker`):
|
||||||
|
// - TILE_PICKER (`T`) : selecciona índex de tile.
|
||||||
|
// - ENTITY_PICKER (`E`) : selecciona tipus d'entitat (nom registrat al
|
||||||
|
// factory).
|
||||||
|
// Mútuament excloents: obrir un tanca l'altre.
|
||||||
class Editor {
|
class Editor {
|
||||||
public:
|
public:
|
||||||
// Entra al mode editor: notifica i marca `active=true`. La congelació
|
// Entra al mode editor: notifica i marca `active=true`. La congelació
|
||||||
@@ -37,29 +48,65 @@ class Editor {
|
|||||||
// Processa input propi (brush, picker). No-op si no actiu.
|
// Processa input propi (brush, picker). No-op si no actiu.
|
||||||
void update(int delta_ms, Tilemap& tilemap);
|
void update(int delta_ms, Tilemap& tilemap);
|
||||||
|
|
||||||
// Pinta el border blau i, més endavant, brush preview + picker.
|
// Pinta el border blau i, segons mode i picker, brush preview, marc
|
||||||
// No-op si no actiu.
|
// d'entitat seleccionada o el picker. No-op si no actiu.
|
||||||
void render(const Tilemap& tilemap) const;
|
void render(const Tilemap& tilemap) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Les branques d'update / render segons el mode (picker obert o
|
// Mode del cursor: determina què fa el clic esquerre sobre el mapa.
|
||||||
// pintura al mapa) queden separades per claredat; no formen part
|
enum class Mode : std::uint8_t { TILE,
|
||||||
// de l'API pública perquè només té sentit cridar-les des d'`update`.
|
ENTITY,
|
||||||
void updateMap(Tilemap& tilemap, int canvas_x, int canvas_y);
|
HAND };
|
||||||
void updatePicker(const Tilemap& tilemap, int canvas_x, int canvas_y);
|
|
||||||
void renderPicker(const Tilemap& tilemap) const;
|
// Quin picker està obert (si n'hi ha). Mútuament excloents.
|
||||||
|
enum class Picker : std::uint8_t { NONE,
|
||||||
|
TILE,
|
||||||
|
ENTITY };
|
||||||
|
|
||||||
|
// Consum de hotkeys pròpies (T, E, H, ESC). Modifica `mode_` / `picker_`.
|
||||||
|
// Es crida al principi d'`update` perquè altres handlers no vegin
|
||||||
|
// tecles ja interpretades pel propi editor.
|
||||||
|
void handleHotkeys();
|
||||||
|
|
||||||
|
// Aplica una tecla i retorna true si l'editor l'ha consumit. Aïllat
|
||||||
|
// per mantenir baixa la complexitat cognitiva de `handleHotkeys` —
|
||||||
|
// l'iteració del buffer queda neta i la lògica de transició viu en
|
||||||
|
// un sol lloc.
|
||||||
|
auto applyHotkey(int scancode) -> bool;
|
||||||
|
|
||||||
|
// Branques d'update segons (mode, picker). Només té sentit cridar-les
|
||||||
|
// des d'`update`.
|
||||||
|
void updateTileBrush(Tilemap& tilemap, int canvas_x, int canvas_y);
|
||||||
|
void updateEntityBrush(int canvas_x, int canvas_y);
|
||||||
|
void updateHand(int canvas_x, int canvas_y);
|
||||||
|
void updateTilePicker(const Tilemap& tilemap, int canvas_x, int canvas_y);
|
||||||
|
void updateEntityPicker(int canvas_x, int canvas_y);
|
||||||
|
|
||||||
|
void renderTilePicker(const Tilemap& tilemap) const;
|
||||||
|
void renderEntityPicker(const Tilemap& tilemap) const;
|
||||||
|
|
||||||
bool active_{false};
|
bool active_{false};
|
||||||
|
Mode mode_{Mode::TILE};
|
||||||
|
Picker picker_{Picker::NONE};
|
||||||
|
|
||||||
// Estat de l'edició (brush + cursor + picker). Es poblen al pas
|
// Cursor en cel·les del tilemap. -1/-1 = fora del mapa.
|
||||||
// següent; els declarem ara perquè `update()` pugui mutar-los i les
|
int cursor_col_{-1};
|
||||||
// signatures queden estables entre hitos.
|
|
||||||
int brush_{-1}; // Índex de tile al tileset; -1 = sense brush.
|
|
||||||
int cursor_col_{-1}; // Cel·la del mapa sota el cursor (-1 = fora).
|
|
||||||
int cursor_row_{-1};
|
int cursor_row_{-1};
|
||||||
bool picker_open_{false};
|
|
||||||
int picker_scroll_{0}; // Fila superior visible del picker (linealitzat).
|
// Brush actiu per Mode::TILE (índex al tileset; -1 = sense brush).
|
||||||
int picker_hover_{-1}; // Índex del tile sota el cursor dins del picker.
|
int brush_tile_{-1};
|
||||||
|
// Estat del TilePicker.
|
||||||
|
int tile_picker_scroll_{0};
|
||||||
|
int tile_picker_hover_{-1};
|
||||||
|
|
||||||
|
// Brush actiu per Mode::ENTITY (nom registrat al factory; "" = cap).
|
||||||
|
std::string brush_entity_;
|
||||||
|
// Estat de l'EntityPicker.
|
||||||
|
int entity_picker_scroll_{0};
|
||||||
|
int entity_picker_hover_{-1}; // Índex a la llista de noms.
|
||||||
|
|
||||||
|
// Selecció en Mode::HAND (índex dins de l'EntityManager; -1 = cap).
|
||||||
|
int hand_selected_idx_{-1};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _DEBUG
|
#endif // _DEBUG
|
||||||
Reference in new issue
Block a user