Files
aee-arcade/source/game/editor/editor.hpp
T

171 lines
6.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#ifdef _DEBUG
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
class Entity;
class EntityManager;
class Surface;
class Tilemap;
// Mode editor modal del GameScene. Només disponible en Debug — en Release
// aquesta classe no existeix (el .cpp es compila a un object buit i cap
// codi del joc final en fa referència).
//
// 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
// (mútuament excloents). L'editor s'encarrega de:
// - Estat propi (mode actiu, brush per mode, picker obert).
// - Input del ratolí i tecles internes (`T`, `E`, `H`, `Supr`, `ESC`).
// - Render del border blau + brush preview + picker.
// - Persistència del tilemap i de les entitats al YAML del repo en sortir.
//
// Modes del cursor (`Mode`):
// - 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 {
public:
// Entra al mode editor: notifica i marca `active=true`. La congelació
// d'entitats / jugador la gestiona GameScene consultant `active()`.
void enter();
// Surt del mode editor: persisteix el tilemap al YAML (writer surgical)
// i notifica. Si la persistència falla, emet la notificació d'error.
void exit(Tilemap& tilemap);
// Helper: enter() si no actiu, exit() si actiu. Útil des de GameScene
// quan detecta la tecla de toggle.
void toggle(Tilemap& tilemap);
[[nodiscard]] auto active() const -> bool { return active_; }
// Processa input propi (brush, picker). No-op si no actiu.
// `entities` cal perquè els modes ENTITY i HAND modifiquen el manager
// en directe (col·loca/mou/borra entitats); en TILE no s'usa.
void update(int delta_ms, Tilemap& tilemap, EntityManager& entities);
// Pinta el border blau i, segons mode i picker, brush preview, marc
// d'entitat seleccionada o el picker. No-op si no actiu.
// `entities` és necessari per al marc d'entitat hovered/seleccionada
// en Mode::HAND; en altres modes no s'usa.
void render(const Tilemap& tilemap, const EntityManager& entities) const;
private:
// Mode del cursor: determina què fa el clic esquerre sobre el mapa.
enum class Mode : std::uint8_t { TILE,
ENTITY,
HAND };
// 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(EntityManager& entities, int canvas_x, int canvas_y);
void updateHand(EntityManager& entities, 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);
// Intent de col·locació segons brush_entity_ a la cel·la del cursor.
// Construeix un node spawn sintètic, fa el snap concret per tipus
// (sarc → rejilla 4×5; door → fila del mur i, de moment, columna
// canònica) i comprova solapaments. No-op si la cel·la està ocupada
// o el snap no és vàlid. Retorna true si s'ha col·locat.
auto tryPlaceEntity(EntityManager& entities) -> bool;
// Mou l'entitat seleccionada a la cel·la del cursor: borra l'antiga
// i crida `addSpawn` amb la nova posició. Reusa la mateixa lògica
// de snap/solapament que la col·locació nova, excloent l'entitat
// que es mou del check de solapament.
auto tryMoveSelected(EntityManager& entities) const -> bool;
// Sub-handlers de `updateHand`. Aïllats per mantenir baixa la
// complexitat cognitiva del bucle d'esdeveniments.
void handleHandLeftClick(EntityManager& entities);
void handleHandRightClick();
// Borra l'entitat seleccionada. La validació de constraints
// (1 puerta, ≥1 sarc) es fa al pas 8.
void deleteSelected(EntityManager& entities);
// Render del marc d'una entitat: 1 px sobre el seu pixel_rect.
static void renderEntityFrame(const Entity& e, std::uint32_t argb);
void renderTilePicker(const Tilemap& tilemap) const;
void renderEntityPicker(const Tilemap& tilemap) const;
bool active_{false};
Mode mode_{Mode::TILE};
Picker picker_{Picker::NONE};
// Cursor en cel·les del tilemap. -1/-1 = fora del mapa.
int cursor_col_{-1};
int cursor_row_{-1};
// Brush actiu per Mode::TILE (índex al tileset; -1 = sense brush).
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.
// Entrada editable del picker. Cada item recorda el nom (clau de
// factory + YAML), el surface del sprite (frame 0 com a icona) i la
// mida de cada frame al GIF — necessària per clipar el primer frame
// quan el GIF és un strip multi-frame.
struct EditableEntity {
std::string name;
std::shared_ptr<Surface> sprite;
int frame_w{0};
int frame_h{0};
};
// Construïda lazy al primer enter() i preservada entre sessions:
// el cost és I/O + parse de N plantilles YAML, però `EntityLoader`
// ja les memoritza, així que rellegir només resoldria per sortida i
// entrada al editor — no val la pena.
void ensureEditableEntities();
std::vector<EditableEntity> editable_entities_;
// Selecció en Mode::HAND. Treballem amb índexs sobre el snapshot de
// `EntityManager::getEntities()` perquè és el que ens dóna l'API
// pública. Després de cada mutació (move/delete) ho recalculem; com
// que l'editor congela el joc, el snapshot només canvia per accions
// pròpies.
int hand_hover_idx_{-1};
int hand_selected_idx_{-1};
};
#endif // _DEBUG