Files
projecte_2026/source/game/editor/map_editor.hpp
2026-04-11 16:25:56 +02:00

265 lines
10 KiB
C++

#pragma once
#ifdef _DEBUG
#include <SDL3/SDL.h>
#include <cstdint> // Para uint8_t
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <vector> // 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<int> 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> room, std::shared_ptr<Player> player, const std::string& room_path, std::shared_ptr<Scoreboard::Data> 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<std::string>;
[[nodiscard]] auto getAnimationCompletions() const -> std::vector<std::string>;
// 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<float, float>;
[[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
// 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> room_;
std::shared_ptr<Player> player_;
std::shared_ptr<Scoreboard::Data> scoreboard_data_;
// Barra de estado del editor
std::unique_ptr<EditorStatusBar> statusbar_;
// Tile picker y mini mapa
TilePicker tile_picker_;
std::unique_ptr<MiniMap> 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