diff --git a/data/console/commands.yaml b/data/console/commands.yaml index 75113f5..8afd701 100644 --- a/data/console/commands.yaml +++ b/data/console/commands.yaml @@ -219,6 +219,15 @@ categories: completions: EDIT: [ON, OFF, REVERT] + - keyword: SET + handler: cmd_set + description: "Set enemy property (editor, select enemy first)" + usage: "SET " + dynamic_completions: true + completions: + SET: [ANIMATION, COLOR, VX, VY, FLIP] + SET FLIP: [ON, OFF] + - name: CHEATS commands: - keyword: CHEAT diff --git a/source/game/editor/editor_statusbar.cpp b/source/game/editor/editor_statusbar.cpp index f6f72dd..9538d59 100644 --- a/source/game/editor/editor_statusbar.cpp +++ b/source/game/editor/editor_statusbar.cpp @@ -44,6 +44,11 @@ void EditorStatusBar::setSelectionInfo(const std::string& info) { selection_info_ = info; } +// Establece el detalle adicional +void EditorStatusBar::setSelectionDetail(const std::string& detail) { + selection_detail_ = detail; +} + // Dibuja los elementos en la surface void EditorStatusBar::fillTexture() { auto previous_renderer = Screen::get()->getRendererSurface(); @@ -65,13 +70,18 @@ void EditorStatusBar::fillTexture() { text->writeColored(LEFT_X, LINE2_Y, toLower("tile:"), LABEL_COLOR); text->writeColored(LEFT_X + 30, LINE2_Y, TILE_X_STR + "," + TILE_Y_STR, VALUE_COLOR); - // Info de selección o indicador de modo editor + // Línea 2 (continuación): info de selección o indicador de modo editor if (!selection_info_.empty()) { - text->writeColored(LEFT_X + 80, LINE2_Y, toLower(selection_info_), stringToColor("bright_yellow")); + text->writeColored(LEFT_X + 60, LINE2_Y, toLower(selection_info_), stringToColor("bright_yellow")); } else { text->writeColored(176, LINE2_Y, toLower("editor"), stringToColor("bright_green")); } + // Línea 3: detalle de la selección (propiedades del enemigo) + if (!selection_detail_.empty()) { + text->writeColored(LEFT_X, LINE3_Y, toLower(selection_detail_), stringToColor("bright_white")); + } + Screen::get()->setRendererSurface(previous_renderer); } diff --git a/source/game/editor/editor_statusbar.hpp b/source/game/editor/editor_statusbar.hpp index a952229..17c9372 100644 --- a/source/game/editor/editor_statusbar.hpp +++ b/source/game/editor/editor_statusbar.hpp @@ -18,6 +18,7 @@ class EditorStatusBar { void update(float delta_time); void setMouseTile(int tile_x, int tile_y); void setSelectionInfo(const std::string& info); + void setSelectionDetail(const std::string& detail); private: void fillTexture(); // Dibuja los elementos en la surface @@ -37,7 +38,8 @@ class EditorStatusBar { std::string room_name_; // Nombre de la habitación int mouse_tile_x_{0}; // Coordenada X del ratón en tiles int mouse_tile_y_{0}; // Coordenada Y del ratón en tiles - std::string selection_info_; // Información de la entidad seleccionada + std::string selection_info_; // Información de la entidad seleccionada (línea 2) + std::string selection_detail_; // Detalle adicional (línea 3) }; #endif // _DEBUG diff --git a/source/game/editor/map_editor.cpp b/source/game/editor/map_editor.cpp index ef7cf51..7e5a4e7 100644 --- a/source/game/editor/map_editor.cpp +++ b/source/game/editor/map_editor.cpp @@ -13,6 +13,7 @@ #include "core/resources/resource_cache.hpp" // Para Resource::Cache #include "core/resources/resource_list.hpp" // Para Resource::List #include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar +#include "game/ui/console.hpp" // Para Console #include "game/editor/room_saver.hpp" // Para RoomSaver #include "game/entities/player.hpp" // Para Player #include "game/gameplay/enemy_manager.hpp" // Para EnemyManager @@ -77,8 +78,9 @@ void MapEditor::enter(std::shared_ptr room, std::shared_ptr player // Crear la barra de estado statusbar_ = std::make_unique(room_->getNumber(), room_->getName()); - // Resetear estado de drag + // Resetear estado drag_ = {}; + selected_enemy_ = -1; active_ = true; std::cout << "MapEditor: ON (room " << room_path_ << ")\n"; @@ -94,7 +96,9 @@ void MapEditor::exit() { Options::cheats.invincible = invincible_before_editor_; player_->setColor(); - // Liberar recursos + // Restaurar prompt de la consola y limpiar estado + selected_enemy_ = -1; + Console::get()->setPrompt("> "); drag_ = {}; statusbar_.reset(); room_.reset(); @@ -161,34 +165,9 @@ void MapEditor::update(float delta_time) { updateDrag(); } - // Actualizar la barra de estado con las coordenadas del ratón y la selección + // Actualizar la barra de estado + updateStatusBarInfo(); if (statusbar_) { - statusbar_->setMouseTile(mouse_tile_x_, mouse_tile_y_); - - // Construir info de selección - std::string sel_info; - if (drag_.target != DragTarget::NONE) { - switch (drag_.target) { - case DragTarget::PLAYER: - sel_info = "PLAYER"; - break; - case DragTarget::ENEMY_INITIAL: - sel_info = "ENEMY " + std::to_string(drag_.index); - break; - case DragTarget::ENEMY_BOUND1: - sel_info = "E" + std::to_string(drag_.index) + " BOUND1"; - break; - case DragTarget::ENEMY_BOUND2: - sel_info = "E" + std::to_string(drag_.index) + " BOUND2"; - break; - case DragTarget::ITEM: - sel_info = "ITEM " + std::to_string(drag_.index); - break; - case DragTarget::NONE: - break; - } - } - statusbar_->setSelectionInfo(sel_info); statusbar_->update(delta_time); } } @@ -235,6 +214,7 @@ void MapEditor::handleMouseDown(float game_x, float game_y) { drag_.offset_y = game_y - entity_y; drag_.snap_x = entity_x; drag_.snap_y = entity_y; + drag_.moved = false; }; // 1. Hit test sobre el jugador (8x16) @@ -281,6 +261,9 @@ void MapEditor::handleMouseDown(float game_x, float game_y) { return; } } + + // Click en el fondo: deseleccionar + selected_enemy_ = -1; } // Procesa soltar el ratón: commit del drag @@ -288,16 +271,29 @@ void MapEditor::handleMouseUp() { if (drag_.target == DragTarget::NONE) { return; } const int IDX = drag_.index; + + // Si no se movió: fue un click → seleccionar/deseleccionar + if (!drag_.moved) { + if (drag_.target == DragTarget::ENEMY_INITIAL) { + // Toggle selección: si ya estaba seleccionado, deseleccionar + selected_enemy_ = (selected_enemy_ == IDX) ? -1 : IDX; + } else { + // Click en otro tipo de entidad: deseleccionar enemigo + selected_enemy_ = -1; + } + drag_ = {}; + return; + } + + // Hubo movimiento: commit del drag const int SNAP_X = static_cast(drag_.snap_x); const int SNAP_Y = static_cast(drag_.snap_y); - bool changed = false; switch (drag_.target) { case DragTarget::PLAYER: player_->setDebugPosition(drag_.snap_x, drag_.snap_y); player_->finalizeDebugTeleport(); - // El jugador no se guarda en el YAML de la habitación (es dato de spawn global) break; case DragTarget::ENEMY_INITIAL: @@ -305,6 +301,7 @@ void MapEditor::handleMouseUp() { room_data_.enemies[IDX].x = drag_.snap_x; room_data_.enemies[IDX].y = drag_.snap_y; room_->getEnemyManager()->getEnemy(IDX)->resetToInitialPosition(room_data_.enemies[IDX]); + selected_enemy_ = IDX; // Seleccionar el enemigo arrastrado changed = true; } break; @@ -313,6 +310,7 @@ void MapEditor::handleMouseUp() { if (IDX >= 0 && IDX < static_cast(room_data_.enemies.size())) { room_data_.enemies[IDX].x1 = SNAP_X; room_data_.enemies[IDX].y1 = SNAP_Y; + selected_enemy_ = IDX; changed = true; } break; @@ -321,6 +319,7 @@ void MapEditor::handleMouseUp() { if (IDX >= 0 && IDX < static_cast(room_data_.enemies.size())) { room_data_.enemies[IDX].x2 = SNAP_X; room_data_.enemies[IDX].y2 = SNAP_Y; + selected_enemy_ = IDX; changed = true; } break; @@ -328,6 +327,7 @@ void MapEditor::handleMouseUp() { case DragTarget::ITEM: if (IDX >= 0 && IDX < room_->getItemManager()->getCount()) { room_->getItemManager()->getItem(IDX)->setPosition(drag_.snap_x, drag_.snap_y); + selected_enemy_ = -1; changed = true; } break; @@ -336,12 +336,7 @@ void MapEditor::handleMouseUp() { break; } - // Auto-guardar si hubo cambio - if (changed) { - autosave(); - } - - // Resetear estado de drag + if (changed) { autosave(); } drag_ = {}; } @@ -350,8 +345,16 @@ void MapEditor::updateDrag() { float raw_x = mouse_game_x_ - drag_.offset_x; float raw_y = mouse_game_y_ - drag_.offset_y; - drag_.snap_x = snapToGrid(raw_x); - drag_.snap_y = snapToGrid(raw_y); + float new_snap_x = snapToGrid(raw_x); + float new_snap_y = snapToGrid(raw_y); + + // Detectar si hubo movimiento real (el snap cambió respecto al inicio) + if (new_snap_x != drag_.snap_x || new_snap_y != drag_.snap_y) { + drag_.moved = true; + } + + drag_.snap_x = new_snap_x; + drag_.snap_y = new_snap_y; // Mientras arrastramos, mover la entidad visualmente a la posición snapped switch (drag_.target) { @@ -390,48 +393,56 @@ void MapEditor::updateDrag() { // Dibuja highlight del elemento seleccionado/arrastrado void MapEditor::renderSelectionHighlight() { - if (drag_.target == DragTarget::NONE) { return; } - auto game_surface = Screen::get()->getRendererSurface(); if (!game_surface) { return; } - const Uint8 HIGHLIGHT_COLOR = stringToColor("bright_white"); constexpr float SZ = static_cast(Tile::SIZE); + // Highlight del enemigo seleccionado (persistente, color bright_green) + if (selected_enemy_ >= 0 && selected_enemy_ < room_->getEnemyManager()->getCount()) { + SDL_FRect enemy_rect = room_->getEnemyManager()->getEnemy(selected_enemy_)->getRect(); + SDL_FRect border = { + .x = enemy_rect.x - 1, + .y = enemy_rect.y - 1, + .w = enemy_rect.w + 2, + .h = enemy_rect.h + 2}; + game_surface->drawRectBorder(&border, stringToColor("bright_green")); + } + + // Highlight del drag activo (temporal, color bright_white) + if (drag_.target == DragTarget::NONE || !drag_.moved) { return; } + + const Uint8 DRAG_COLOR = stringToColor("bright_white"); SDL_FRect highlight_rect{}; + switch (drag_.target) { case DragTarget::PLAYER: highlight_rect = player_->getRect(); break; - case DragTarget::ENEMY_INITIAL: if (drag_.index >= 0 && drag_.index < room_->getEnemyManager()->getCount()) { highlight_rect = room_->getEnemyManager()->getEnemy(drag_.index)->getRect(); } break; - case DragTarget::ENEMY_BOUND1: case DragTarget::ENEMY_BOUND2: highlight_rect = {.x = drag_.snap_x, .y = drag_.snap_y, .w = SZ, .h = SZ}; break; - case DragTarget::ITEM: if (drag_.index >= 0 && drag_.index < room_->getItemManager()->getCount()) { highlight_rect = room_->getItemManager()->getItem(drag_.index)->getCollider(); } break; - case DragTarget::NONE: return; } - // Dibujar rectángulo de highlight alrededor de la entidad SDL_FRect border = { .x = highlight_rect.x - 1, .y = highlight_rect.y - 1, .w = highlight_rect.w + 2, .h = highlight_rect.h + 2}; - game_surface->drawRectBorder(&border, HIGHLIGHT_COLOR); + game_surface->drawRectBorder(&border, DRAG_COLOR); } // Alinea un valor a la cuadrícula de 8x8 @@ -523,4 +534,131 @@ void MapEditor::updateMousePosition() { if (mouse_tile_y_ >= PlayArea::HEIGHT / Tile::SIZE) { mouse_tile_y_ = PlayArea::HEIGHT / Tile::SIZE - 1; } } +// Actualiza la información de la barra de estado +void MapEditor::updateStatusBarInfo() { + if (!statusbar_) { return; } + + statusbar_->setMouseTile(mouse_tile_x_, mouse_tile_y_); + + std::string sel_info; + std::string sel_detail; + + // Info de drag activo (prioridad) + if (drag_.target != DragTarget::NONE && drag_.moved) { + switch (drag_.target) { + case DragTarget::PLAYER: sel_info = "player"; break; + case DragTarget::ENEMY_INITIAL: sel_info = "enemy " + std::to_string(drag_.index); break; + case DragTarget::ENEMY_BOUND1: sel_info = "e" + std::to_string(drag_.index) + " bound1"; break; + case DragTarget::ENEMY_BOUND2: sel_info = "e" + std::to_string(drag_.index) + " bound2"; break; + case DragTarget::ITEM: sel_info = "item " + std::to_string(drag_.index); break; + case DragTarget::NONE: break; + } + } + // Info de enemigo seleccionado (persistente) + else if (selected_enemy_ >= 0 && selected_enemy_ < static_cast(room_data_.enemies.size())) { + const auto& e = room_data_.enemies[selected_enemy_]; + // Extraer nombre corto de la animación (sin .yaml) + std::string anim = e.animation_path; + auto dot = anim.rfind('.'); + if (dot != std::string::npos) { anim = anim.substr(0, dot); } + + sel_info = "enemy " + std::to_string(selected_enemy_) + ": " + anim; + sel_detail = e.color + " vx:" + std::to_string(static_cast(e.vx)) + + " vy:" + std::to_string(static_cast(e.vy)); + if (e.flip) { sel_detail += " flip"; } + } + + statusbar_->setSelectionInfo(sel_info); + statusbar_->setSelectionDetail(sel_detail); + + // Actualizar el prompt de la consola según la selección + if (selected_enemy_ >= 0) { + Console::get()->setPrompt("enemy " + std::to_string(selected_enemy_) + "> "); + } else { + Console::get()->setPrompt("> "); + } +} + +// ¿Hay un enemigo seleccionado? +auto MapEditor::hasSelectedEnemy() const -> bool { + return selected_enemy_ >= 0 && selected_enemy_ < static_cast(room_data_.enemies.size()); +} + +// Modifica una propiedad del enemigo seleccionado +auto MapEditor::setEnemyProperty(const std::string& property, const std::string& value) -> std::string { + if (!active_) { return "Editor not active"; } + if (!hasSelectedEnemy()) { return "No enemy selected"; } + + auto& enemy = room_data_.enemies[selected_enemy_]; + + if (property == "ANIMATION") { + std::string anim = toLower(value); + if (anim.find('.') == std::string::npos) { anim += ".yaml"; } + + // Intentar recrear el enemigo con la nueva animación + std::string old_anim = enemy.animation_path; + enemy.animation_path = anim; + try { + auto* enemy_mgr = room_->getEnemyManager(); + enemy_mgr->getEnemy(selected_enemy_) = std::make_shared(enemy); + } catch (const std::exception& e) { + enemy.animation_path = old_anim; // Restaurar si falla + return std::string("Error: ") + e.what(); + } + + autosave(); + return "animation: " + anim; + } + + if (property == "COLOR") { + std::string color = toLower(value); + + // Intentar recrear el enemigo con el nuevo color + std::string old_color = enemy.color; + enemy.color = color; + try { + auto* enemy_mgr = room_->getEnemyManager(); + enemy_mgr->getEnemy(selected_enemy_) = std::make_shared(enemy); + } catch (const std::exception& e) { + enemy.color = old_color; + return std::string("Error: ") + e.what(); + } + + autosave(); + return "color: " + color; + } + + if (property == "VX") { + try { + enemy.vx = std::stof(value); + } catch (...) { return "Invalid value: " + value; } + enemy.vy = 0.0F; // No se permiten velocidades en los dos ejes + + room_->getEnemyManager()->getEnemy(selected_enemy_)->resetToInitialPosition(enemy); + autosave(); + return "vx: " + std::to_string(static_cast(enemy.vx)) + " vy: 0"; + } + + if (property == "VY") { + try { + enemy.vy = std::stof(value); + } catch (...) { return "Invalid value: " + value; } + enemy.vx = 0.0F; // No se permiten velocidades en los dos ejes + + room_->getEnemyManager()->getEnemy(selected_enemy_)->resetToInitialPosition(enemy); + autosave(); + return "vy: " + std::to_string(static_cast(enemy.vy)) + " vx: 0"; + } + + if (property == "FLIP") { + enemy.flip = (value == "ON" || value == "TRUE" || value == "1"); + + room_->getEnemyManager()->getEnemy(selected_enemy_)->resetToInitialPosition(enemy); + autosave(); + return std::string("flip: ") + (enemy.flip ? "on" : "off"); + } + + return "Unknown property: " + property + " (use: animation, color, vx, vy, flip)"; +} + #endif // _DEBUG diff --git a/source/game/editor/map_editor.hpp b/source/game/editor/map_editor.hpp index 34e5368..564c230 100644 --- a/source/game/editor/map_editor.hpp +++ b/source/game/editor/map_editor.hpp @@ -30,7 +30,11 @@ class MapEditor { void update(float delta_time); void render(); void handleEvent(const SDL_Event& event); - auto revert() -> std::string; // Revierte todos los cambios al estado original + auto revert() -> std::string; + + // Comandos SET para modificar el enemigo seleccionado (llamados desde console_commands) + auto setEnemyProperty(const std::string& property, const std::string& value) -> std::string; + [[nodiscard]] auto hasSelectedEnemy() const -> bool; private: static MapEditor* instance_; // [SINGLETON] Objeto privado @@ -38,49 +42,47 @@ class MapEditor { MapEditor(); // Constructor ~MapEditor(); // Destructor - // Tipos para drag & drop - enum class DragTarget { NONE, - PLAYER, - ENEMY_INITIAL, - ENEMY_BOUND1, - ENEMY_BOUND2, - ITEM }; + // Tipos para drag & drop y selección + enum class DragTarget { NONE, PLAYER, ENEMY_INITIAL, ENEMY_BOUND1, ENEMY_BOUND2, ITEM }; struct DragState { DragTarget target{DragTarget::NONE}; - int index{-1}; // Índice del enemigo o item en room_data_ - float offset_x{0.0F}; // Offset entre el cursor y el origen de la entidad al inicio del drag + int index{-1}; + float offset_x{0.0F}; float offset_y{0.0F}; - float snap_x{0.0F}; // Posición snapped actual (para preview) + 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(); // Convierte coordenadas de ventana a coordenadas de juego y tile - void renderEnemyBoundaries(); // Dibuja marcadores de boundaries y líneas de ruta - void renderBoundaryMarker(float x, float y, Uint8 color); // Dibuja un marcador de boundary en una posición - void renderSelectionHighlight(); // Dibuja highlight del elemento seleccionado/arrastrado - void handleMouseDown(float game_x, float game_y); // Procesa click del ratón (hit test + inicio de drag) - void handleMouseUp(); // Procesa soltar el ratón (commit del drag + autosave) - void updateDrag(); // Actualiza la posición snapped durante el drag - void autosave(); // Guarda los cambios puntuales al YAML - static auto snapToGrid(float value) -> float; // Alinea un valor a la cuadrícula de 8x8 - static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool; // Hit test punto en rectángulo + void updateMousePosition(); + void renderEnemyBoundaries(); + void renderBoundaryMarker(float x, float y, Uint8 color); + void renderSelectionHighlight(); + void handleMouseDown(float game_x, float game_y); + void handleMouseUp(); + void updateDrag(); + void autosave(); + void updateStatusBarInfo(); + static auto snapToGrid(float value) -> float; + static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool; // Estado del editor bool active_{false}; DragState drag_; + int selected_enemy_{-1}; // Índice del enemigo seleccionado (-1 = ninguno) // Datos de la habitación - Room::Data room_data_; // Copia mutable (para boundaries y edición) - std::string room_path_; // Nombre del fichero (ej: "06.yaml") - std::string file_path_; // Ruta completa del fichero en disco + Room::Data room_data_; + std::string room_path_; + std::string file_path_; - // YAML: nodo vivo (se edita parcialmente) y backup para revert - fkyaml::node yaml_; // Nodo YAML actual (editado parcialmente) - fkyaml::node yaml_backup_; // Backup del YAML original al entrar + // YAML: nodo original (para campos que no se editan: name_ca, etc.) + fkyaml::node yaml_; + fkyaml::node yaml_backup_; - // Referencias a objetos vivos (para rendering) + // Referencias a objetos vivos std::shared_ptr room_; std::shared_ptr player_; std::shared_ptr scoreboard_data_; @@ -88,13 +90,13 @@ class MapEditor { // Barra de estado del editor std::unique_ptr statusbar_; - // Estado del ratón en coordenadas de juego + // 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 de invencibilidad (para restaurar al salir) + // Estado previo de invencibilidad Options::Cheat::State invincible_before_editor_{Options::Cheat::State::DISABLED}; }; diff --git a/source/game/ui/console.cpp b/source/game/ui/console.cpp index 4bfac2b..2712cf3 100644 --- a/source/game/ui/console.cpp +++ b/source/game/ui/console.cpp @@ -157,7 +157,7 @@ void Console::redrawText() { // Línea de input (siempre la última) const bool SHOW_CURSOR = cursor_visible_ && (static_cast(input_line_.size()) < MAX_LINE_CHARS); - const std::string INPUT_STR = "> " + input_line_ + (SHOW_CURSOR ? "_" : ""); + const std::string INPUT_STR = prompt_ + input_line_ + (SHOW_CURSOR ? "_" : ""); text_->writeColored(PADDING_IN_H, y_pos, INPUT_STR, BORDER_COLOR); Screen::get()->setRendererSurface(previous_renderer); diff --git a/source/game/ui/console.hpp b/source/game/ui/console.hpp index 39f5d37..aa76e04 100644 --- a/source/game/ui/console.hpp +++ b/source/game/ui/console.hpp @@ -32,6 +32,9 @@ class Console { auto getVisibleHeight() -> int; // Píxeles visibles actuales (0 = oculta, height_ = totalmente visible) [[nodiscard]] auto getText() const -> std::shared_ptr { return text_; } + // Prompt configurable (por defecto "> ") + void setPrompt(const std::string& prompt) { prompt_ = prompt; } + // Callback llamado al abrir (true) o cerrar (false) la consola std::function on_toggle; @@ -84,6 +87,7 @@ class Console { // Estado de la entrada de texto std::vector msg_lines_; // Líneas de mensaje (1 o más) std::string input_line_; + std::string prompt_{"> "}; // Prompt configurable float cursor_timer_{0.0F}; bool cursor_visible_{true}; diff --git a/source/game/ui/console_commands.cpp b/source/game/ui/console_commands.cpp index 18d0dd5..d700122 100644 --- a/source/game/ui/console_commands.cpp +++ b/source/game/ui/console_commands.cpp @@ -14,6 +14,7 @@ #include "core/rendering/render_info.hpp" // Para RenderInfo #include "core/rendering/screen.hpp" // Para Screen #include "core/resources/resource_helper.hpp" // Para Resource::Helper +#include "core/resources/resource_list.hpp" // Para Resource::List #include "external/fkyaml_node.hpp" // Para fkyaml::node #include "game/game_control.hpp" // Para GameControl #include "game/options.hpp" // Para Options @@ -703,6 +704,14 @@ static auto cmd_edit(const std::vector& args) -> std::string { } return "usage: edit [on|off|revert]"; } + +// SET — modifica propiedad del enemigo seleccionado en el editor +static auto cmd_set(const std::vector& args) -> std::string { + if (!MapEditor::get() || !MapEditor::get()->isActive()) { return "Editor not active"; } + if (!MapEditor::get()->hasSelectedEnemy()) { return "No enemy selected"; } + if (args.size() < 2) { return "usage: set "; } + return MapEditor::get()->setEnemyProperty(args[0], args[1]); +} #endif // SHOW [INFO|NOTIFICATION|CHEEVO] @@ -904,6 +913,7 @@ void CommandRegistry::registerHandlers() { handlers_["cmd_room"] = cmd_room; handlers_["cmd_scene"] = cmd_scene; handlers_["cmd_edit"] = cmd_edit; + handlers_["cmd_set"] = cmd_set; #endif // HELP se registra en load() como lambda que captura this @@ -930,6 +940,29 @@ void CommandRegistry::registerHandlers() { } return result; }; + +#ifdef _DEBUG + // SET COLOR: colores de la paleta (UPPERCASE, sin .yaml) + dynamic_providers_["SET COLOR"] = []() -> std::vector { + return {"BLACK", "BRIGHT_BLACK", "BLUE", "BRIGHT_BLUE", "RED", "BRIGHT_RED", + "MAGENTA", "BRIGHT_MAGENTA", "GREEN", "BRIGHT_GREEN", "CYAN", "BRIGHT_CYAN", + "YELLOW", "BRIGHT_YELLOW", "WHITE", "BRIGHT_WHITE"}; + }; + + // SET ANIMATION: animaciones de enemigos (nombres sin extensión, UPPERCASE) + dynamic_providers_["SET ANIMATION"] = []() -> std::vector { + std::vector result; + auto list = Resource::List::get()->getListByType(Resource::List::Type::ANIMATION); + for (const auto& path : list) { + if (path.find("enemies") == std::string::npos) { continue; } + std::string name = getFileName(path); + auto dot = name.rfind('.'); + if (dot != std::string::npos) { name = name.substr(0, dot); } + result.push_back(toUpper(name)); + } + return result; + }; +#endif } void CommandRegistry::load(const std::string& yaml_path) {