#pragma once #ifdef _DEBUG #include #include // Para uint8_t #include // Para shared_ptr, unique_ptr #include // Para string #include // Para vector #include "game/editor/mini_map.hpp" // Para MiniMap #include "game/editor/tile_picker.hpp" // Para TilePicker #include "game/entities/door.hpp" // Para Door::Data #include "game/entities/enemy.hpp" // Para Enemy::Data #include "game/entities/item.hpp" // Para Item::Data #include "game/entities/key.hpp" // Para Key::Data #include "game/entities/moving_platform.hpp" // Para MovingPlatform::Data #include "game/entities/player.hpp" // Para Player::SpawnData #include "game/gameplay/room.hpp" // Para Room::Data #include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data #include "game/options.hpp" // Para Options::Cheat class EditorStatusBar; // Tipo de entidad editable en el editor enum class EntityType : std::uint8_t { NONE, ENEMY, ITEM, PLATFORM, KEY, DOOR }; // Seleccion unificada: una sola entidad seleccionada a la vez struct Selection { EntityType type{EntityType::NONE}; int index{-1}; void clear() { type = EntityType::NONE; index = -1; } [[nodiscard]] auto isNone() const -> bool { return type == EntityType::NONE; } [[nodiscard]] auto is(EntityType t) const -> bool { return type == t && index >= 0; } }; // Brush rectangular para pintar tiles. Una sola estructura cubre: // - brush vacío (width=0, height=0) // - single tile (width=1, height=1, tiles={N}) // - borrador explícito (width=1, height=1, tiles={ERASE}) // - patrón rectangular sampleado del nivel o del tileset struct BrushPattern { static constexpr int TRANSPARENT = -2; // celda no se escribe al estampar static constexpr int ERASE = -1; // celda escribe -1 (vacía el destino) int width{0}; int height{0}; std::vector tiles; // size = width*height, row-major [[nodiscard]] auto isEmpty() const -> bool { return width <= 0 || height <= 0; } [[nodiscard]] auto at(int dx, int dy) const -> int { return tiles[(dy * width) + dx]; } void clear() { width = 0; height = 0; tiles.clear(); } void setSingle(int tile) { width = 1; height = 1; tiles = {tile}; } }; // Estado del eyedropper (clic derecho con drag para samplear el nivel) struct EyedropperState { bool active{false}; int start_tile_x{0}; int start_tile_y{0}; }; class MapEditor { public: static void init(); // [SINGLETON] Crea el objeto static void destroy(); // [SINGLETON] Destruye el objeto static auto get() -> MapEditor*; // [SINGLETON] Obtiene el objeto void enter(std::shared_ptr room, std::shared_ptr player, const std::string& room_path, std::shared_ptr scoreboard_data); void exit(); [[nodiscard]] auto isActive() const -> bool { return active_; } void update(float delta_time); void render(); void handleEvent(const SDL_Event& event); auto revert() -> std::string; // Comandos para enemigos (llamados desde console_commands) auto setEnemyProperty(const std::string& property, const std::string& value) -> std::string; auto addEnemy() -> std::string; auto deleteEnemy() -> std::string; auto duplicateEnemy() -> std::string; [[nodiscard]] auto hasSelectedEnemy() const -> bool { return selection_.is(EntityType::ENEMY); } [[nodiscard]] auto getSetCompletions() const -> std::vector; [[nodiscard]] auto getAnimationCompletions() const -> std::vector; // Comandos para propiedades de la habitación auto setRoomProperty(const std::string& property, const std::string& value) -> std::string; auto createNewRoom(const std::string& direction = "") -> std::string; auto deleteRoom() -> std::string; // Opciones del editor (llamados desde console_commands / teclas) static auto showInfo(bool show) -> std::string; auto showGrid(bool show) -> std::string; auto setEditingCollision(bool collision) -> std::string; [[nodiscard]] auto isGridEnabled() const -> bool { return settings_.grid; } void toggleMiniMap(); void setReenter(bool value) { reenter_ = value; } auto setMiniMapBg(const std::string& color) -> std::string; auto setMiniMapConn(const std::string& color) -> std::string; // Comandos para items auto setItemProperty(const std::string& property, const std::string& value) -> std::string; auto addItem() -> std::string; auto deleteItem() -> std::string; auto duplicateItem() -> std::string; [[nodiscard]] auto hasSelectedItem() const -> bool { return selection_.is(EntityType::ITEM); } void openTilePicker(const std::string& tileset_name, int current_tile); // Comandos para plataformas auto setPlatformProperty(const std::string& property, const std::string& value) -> std::string; auto addPlatform() -> std::string; auto deletePlatform() -> std::string; auto duplicatePlatform() -> std::string; [[nodiscard]] auto hasSelectedPlatform() const -> bool { return selection_.is(EntityType::PLATFORM); } // Comandos para llaves auto setKeyProperty(const std::string& property, const std::string& value) -> std::string; auto addKey() -> std::string; auto deleteKey() -> std::string; auto duplicateKey() -> std::string; [[nodiscard]] auto hasSelectedKey() const -> bool { return selection_.is(EntityType::KEY); } // Comandos para puertas auto setDoorProperty(const std::string& property, const std::string& value) -> std::string; auto addDoor() -> std::string; auto deleteDoor() -> std::string; auto duplicateDoor() -> std::string; [[nodiscard]] auto hasSelectedDoor() const -> bool { return selection_.is(EntityType::DOOR); } // Seleccion unificada [[nodiscard]] auto getSelectionType() const -> EntityType { return selection_.type; } private: static MapEditor* instance_; // NOLINT(readability-identifier-naming) [SINGLETON] Objeto privado MapEditor(); // Constructor ~MapEditor(); // Destructor // Opciones persistentes del editor struct Settings { bool grid{false}; Uint8 minimap_bg{2}; Uint8 minimap_conn{14}; }; Settings settings_; void loadSettings(); void saveSettings() const; // Tipos para drag & drop enum class DragTarget : std::uint8_t { NONE, PLAYER, ENTITY_INITIAL, ENTITY_BOUND1, ENTITY_BOUND2 }; struct DragState { DragTarget target{DragTarget::NONE}; EntityType entity_type{EntityType::NONE}; int index{-1}; float offset_x{0.0F}; float offset_y{0.0F}; float snap_x{0.0F}; float snap_y{0.0F}; bool moved{false}; // true si el ratón se movió durante el drag }; // Métodos internos void updateMousePosition(); void renderCollisionOverlay() const; void renderEntityBoundaries(); static void renderBoundaryMarker(float x, float y, Uint8 color); void renderSelectionHighlight(); void renderBrushPreview(); void renderEyedropperRect() const; static void renderGrid(); void handleMouseDown(float game_x, float game_y); void handleMouseUp(); void stampBrushAt(int tile_x, int tile_y); void commitEyedropper(); [[nodiscard]] auto sampleBrush(int x1, int y1, int x2, int y2) const -> BrushPattern; [[nodiscard]] static auto buildPatternFromTileset(const std::string& tileset_name, int col, int row, int width, int height) -> BrushPattern; // Reconstruye todas las puertas vivas desde room_data_, limpiando primero // los WALLs antiguos del CollisionMap. Lo usa setDoorProperty cuando un // cambio (id, animation) requiere recrear el Door y mantener los tiles // sincronizados. void rebuildDoors(); void updateDrag(); auto commitEntityDrag() -> bool; void moveEntityVisual(); void autosave(); void updateStatusBarInfo(); static auto snapToGrid(float value) -> float; static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool; // Entity helpers: acceso abstracto a datos de entidad por tipo struct BoundaryData { int x1, y1, x2, y2; }; [[nodiscard]] auto entityCount(EntityType type) const -> int; auto entityRect(EntityType type, int index) -> SDL_FRect; static auto entityHasBoundaries(EntityType type) -> bool; [[nodiscard]] auto entityBoundaries(EntityType type, int index) const -> BoundaryData; [[nodiscard]] auto entityPosition(EntityType type, int index) const -> std::pair; [[nodiscard]] auto entityDataCount(EntityType type) const -> int; static auto entityLabel(EntityType type) -> const char*; // Estado del editor bool active_{false}; DragState drag_; Selection selection_; // Entidad seleccionada (unificada: enemy, item o platform) BrushPattern brush_; // Brush activo (vacío = sin brush) EyedropperState eyedropper_; // Estado del eyedropper (clic derecho) bool painting_{false}; // true mientras se está pintando con click izquierdo mantenido bool editing_collision_{false}; // true = editando collision tilemap, false = editando draw tilemap int auto_collision_{-1}; // -1 = desactivado, 1 = WALL, 2 = PASSABLE (se aplica al pintar draw tiles) // Datos de la habitación Room::Data room_data_; std::string room_path_; std::string file_path_; // Referencias a objetos vivos std::shared_ptr room_; std::shared_ptr player_; std::shared_ptr scoreboard_data_; // Barra de estado del editor std::unique_ptr statusbar_; // Tile picker y mini mapa TilePicker tile_picker_; std::unique_ptr mini_map_; bool mini_map_visible_{false}; // Estado del ratón float mouse_game_x_{0.0F}; float mouse_game_y_{0.0F}; int mouse_tile_x_{0}; int mouse_tile_y_{0}; // Estado previo (para restaurar al salir) Options::Cheat::State invincible_before_editor_{Options::Cheat::State::DISABLED}; bool reenter_{false}; // true cuando es un re-enter tras cambio de room }; #endif // _DEBUG