editar propietats del enemic
This commit is contained in:
@@ -219,6 +219,15 @@ categories:
|
|||||||
completions:
|
completions:
|
||||||
EDIT: [ON, OFF, REVERT]
|
EDIT: [ON, OFF, REVERT]
|
||||||
|
|
||||||
|
- keyword: SET
|
||||||
|
handler: cmd_set
|
||||||
|
description: "Set enemy property (editor, select enemy first)"
|
||||||
|
usage: "SET <ANIMATION|COLOR|VX|VY|FLIP> <value>"
|
||||||
|
dynamic_completions: true
|
||||||
|
completions:
|
||||||
|
SET: [ANIMATION, COLOR, VX, VY, FLIP]
|
||||||
|
SET FLIP: [ON, OFF]
|
||||||
|
|
||||||
- name: CHEATS
|
- name: CHEATS
|
||||||
commands:
|
commands:
|
||||||
- keyword: CHEAT
|
- keyword: CHEAT
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ void EditorStatusBar::setSelectionInfo(const std::string& info) {
|
|||||||
selection_info_ = info;
|
selection_info_ = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Establece el detalle adicional
|
||||||
|
void EditorStatusBar::setSelectionDetail(const std::string& detail) {
|
||||||
|
selection_detail_ = detail;
|
||||||
|
}
|
||||||
|
|
||||||
// Dibuja los elementos en la surface
|
// Dibuja los elementos en la surface
|
||||||
void EditorStatusBar::fillTexture() {
|
void EditorStatusBar::fillTexture() {
|
||||||
auto previous_renderer = Screen::get()->getRendererSurface();
|
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, LINE2_Y, toLower("tile:"), LABEL_COLOR);
|
||||||
text->writeColored(LEFT_X + 30, LINE2_Y, TILE_X_STR + "," + TILE_Y_STR, VALUE_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()) {
|
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 {
|
} else {
|
||||||
text->writeColored(176, LINE2_Y, toLower("editor"), stringToColor("bright_green"));
|
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);
|
Screen::get()->setRendererSurface(previous_renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class EditorStatusBar {
|
|||||||
void update(float delta_time);
|
void update(float delta_time);
|
||||||
void setMouseTile(int tile_x, int tile_y);
|
void setMouseTile(int tile_x, int tile_y);
|
||||||
void setSelectionInfo(const std::string& info);
|
void setSelectionInfo(const std::string& info);
|
||||||
|
void setSelectionDetail(const std::string& detail);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void fillTexture(); // Dibuja los elementos en la surface
|
void fillTexture(); // Dibuja los elementos en la surface
|
||||||
@@ -37,7 +38,8 @@ class EditorStatusBar {
|
|||||||
std::string room_name_; // Nombre de la habitación
|
std::string room_name_; // Nombre de la habitación
|
||||||
int mouse_tile_x_{0}; // Coordenada X del ratón en tiles
|
int mouse_tile_x_{0}; // Coordenada X del ratón en tiles
|
||||||
int mouse_tile_y_{0}; // Coordenada Y 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
|
#endif // _DEBUG
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "core/resources/resource_cache.hpp" // Para Resource::Cache
|
#include "core/resources/resource_cache.hpp" // Para Resource::Cache
|
||||||
#include "core/resources/resource_list.hpp" // Para Resource::List
|
#include "core/resources/resource_list.hpp" // Para Resource::List
|
||||||
#include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar
|
#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/editor/room_saver.hpp" // Para RoomSaver
|
||||||
#include "game/entities/player.hpp" // Para Player
|
#include "game/entities/player.hpp" // Para Player
|
||||||
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
|
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
|
||||||
@@ -77,8 +78,9 @@ void MapEditor::enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player
|
|||||||
// Crear la barra de estado
|
// Crear la barra de estado
|
||||||
statusbar_ = std::make_unique<EditorStatusBar>(room_->getNumber(), room_->getName());
|
statusbar_ = std::make_unique<EditorStatusBar>(room_->getNumber(), room_->getName());
|
||||||
|
|
||||||
// Resetear estado de drag
|
// Resetear estado
|
||||||
drag_ = {};
|
drag_ = {};
|
||||||
|
selected_enemy_ = -1;
|
||||||
|
|
||||||
active_ = true;
|
active_ = true;
|
||||||
std::cout << "MapEditor: ON (room " << room_path_ << ")\n";
|
std::cout << "MapEditor: ON (room " << room_path_ << ")\n";
|
||||||
@@ -94,7 +96,9 @@ void MapEditor::exit() {
|
|||||||
Options::cheats.invincible = invincible_before_editor_;
|
Options::cheats.invincible = invincible_before_editor_;
|
||||||
player_->setColor();
|
player_->setColor();
|
||||||
|
|
||||||
// Liberar recursos
|
// Restaurar prompt de la consola y limpiar estado
|
||||||
|
selected_enemy_ = -1;
|
||||||
|
Console::get()->setPrompt("> ");
|
||||||
drag_ = {};
|
drag_ = {};
|
||||||
statusbar_.reset();
|
statusbar_.reset();
|
||||||
room_.reset();
|
room_.reset();
|
||||||
@@ -161,34 +165,9 @@ void MapEditor::update(float delta_time) {
|
|||||||
updateDrag();
|
updateDrag();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar la barra de estado con las coordenadas del ratón y la selección
|
// Actualizar la barra de estado
|
||||||
|
updateStatusBarInfo();
|
||||||
if (statusbar_) {
|
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);
|
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_.offset_y = game_y - entity_y;
|
||||||
drag_.snap_x = entity_x;
|
drag_.snap_x = entity_x;
|
||||||
drag_.snap_y = entity_y;
|
drag_.snap_y = entity_y;
|
||||||
|
drag_.moved = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1. Hit test sobre el jugador (8x16)
|
// 1. Hit test sobre el jugador (8x16)
|
||||||
@@ -281,6 +261,9 @@ void MapEditor::handleMouseDown(float game_x, float game_y) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Click en el fondo: deseleccionar
|
||||||
|
selected_enemy_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Procesa soltar el ratón: commit del drag
|
// Procesa soltar el ratón: commit del drag
|
||||||
@@ -288,16 +271,29 @@ void MapEditor::handleMouseUp() {
|
|||||||
if (drag_.target == DragTarget::NONE) { return; }
|
if (drag_.target == DragTarget::NONE) { return; }
|
||||||
|
|
||||||
const int IDX = drag_.index;
|
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<int>(drag_.snap_x);
|
const int SNAP_X = static_cast<int>(drag_.snap_x);
|
||||||
const int SNAP_Y = static_cast<int>(drag_.snap_y);
|
const int SNAP_Y = static_cast<int>(drag_.snap_y);
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
switch (drag_.target) {
|
switch (drag_.target) {
|
||||||
case DragTarget::PLAYER:
|
case DragTarget::PLAYER:
|
||||||
player_->setDebugPosition(drag_.snap_x, drag_.snap_y);
|
player_->setDebugPosition(drag_.snap_x, drag_.snap_y);
|
||||||
player_->finalizeDebugTeleport();
|
player_->finalizeDebugTeleport();
|
||||||
// El jugador no se guarda en el YAML de la habitación (es dato de spawn global)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DragTarget::ENEMY_INITIAL:
|
case DragTarget::ENEMY_INITIAL:
|
||||||
@@ -305,6 +301,7 @@ void MapEditor::handleMouseUp() {
|
|||||||
room_data_.enemies[IDX].x = drag_.snap_x;
|
room_data_.enemies[IDX].x = drag_.snap_x;
|
||||||
room_data_.enemies[IDX].y = drag_.snap_y;
|
room_data_.enemies[IDX].y = drag_.snap_y;
|
||||||
room_->getEnemyManager()->getEnemy(IDX)->resetToInitialPosition(room_data_.enemies[IDX]);
|
room_->getEnemyManager()->getEnemy(IDX)->resetToInitialPosition(room_data_.enemies[IDX]);
|
||||||
|
selected_enemy_ = IDX; // Seleccionar el enemigo arrastrado
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -313,6 +310,7 @@ void MapEditor::handleMouseUp() {
|
|||||||
if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
|
if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
|
||||||
room_data_.enemies[IDX].x1 = SNAP_X;
|
room_data_.enemies[IDX].x1 = SNAP_X;
|
||||||
room_data_.enemies[IDX].y1 = SNAP_Y;
|
room_data_.enemies[IDX].y1 = SNAP_Y;
|
||||||
|
selected_enemy_ = IDX;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -321,6 +319,7 @@ void MapEditor::handleMouseUp() {
|
|||||||
if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
|
if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
|
||||||
room_data_.enemies[IDX].x2 = SNAP_X;
|
room_data_.enemies[IDX].x2 = SNAP_X;
|
||||||
room_data_.enemies[IDX].y2 = SNAP_Y;
|
room_data_.enemies[IDX].y2 = SNAP_Y;
|
||||||
|
selected_enemy_ = IDX;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -328,6 +327,7 @@ void MapEditor::handleMouseUp() {
|
|||||||
case DragTarget::ITEM:
|
case DragTarget::ITEM:
|
||||||
if (IDX >= 0 && IDX < room_->getItemManager()->getCount()) {
|
if (IDX >= 0 && IDX < room_->getItemManager()->getCount()) {
|
||||||
room_->getItemManager()->getItem(IDX)->setPosition(drag_.snap_x, drag_.snap_y);
|
room_->getItemManager()->getItem(IDX)->setPosition(drag_.snap_x, drag_.snap_y);
|
||||||
|
selected_enemy_ = -1;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -336,12 +336,7 @@ void MapEditor::handleMouseUp() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-guardar si hubo cambio
|
if (changed) { autosave(); }
|
||||||
if (changed) {
|
|
||||||
autosave();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resetear estado de drag
|
|
||||||
drag_ = {};
|
drag_ = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,8 +345,16 @@ void MapEditor::updateDrag() {
|
|||||||
float raw_x = mouse_game_x_ - drag_.offset_x;
|
float raw_x = mouse_game_x_ - drag_.offset_x;
|
||||||
float raw_y = mouse_game_y_ - drag_.offset_y;
|
float raw_y = mouse_game_y_ - drag_.offset_y;
|
||||||
|
|
||||||
drag_.snap_x = snapToGrid(raw_x);
|
float new_snap_x = snapToGrid(raw_x);
|
||||||
drag_.snap_y = snapToGrid(raw_y);
|
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
|
// Mientras arrastramos, mover la entidad visualmente a la posición snapped
|
||||||
switch (drag_.target) {
|
switch (drag_.target) {
|
||||||
@@ -390,48 +393,56 @@ void MapEditor::updateDrag() {
|
|||||||
|
|
||||||
// Dibuja highlight del elemento seleccionado/arrastrado
|
// Dibuja highlight del elemento seleccionado/arrastrado
|
||||||
void MapEditor::renderSelectionHighlight() {
|
void MapEditor::renderSelectionHighlight() {
|
||||||
if (drag_.target == DragTarget::NONE) { return; }
|
|
||||||
|
|
||||||
auto game_surface = Screen::get()->getRendererSurface();
|
auto game_surface = Screen::get()->getRendererSurface();
|
||||||
if (!game_surface) { return; }
|
if (!game_surface) { return; }
|
||||||
|
|
||||||
const Uint8 HIGHLIGHT_COLOR = stringToColor("bright_white");
|
|
||||||
constexpr float SZ = static_cast<float>(Tile::SIZE);
|
constexpr float SZ = static_cast<float>(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{};
|
SDL_FRect highlight_rect{};
|
||||||
|
|
||||||
switch (drag_.target) {
|
switch (drag_.target) {
|
||||||
case DragTarget::PLAYER:
|
case DragTarget::PLAYER:
|
||||||
highlight_rect = player_->getRect();
|
highlight_rect = player_->getRect();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DragTarget::ENEMY_INITIAL:
|
case DragTarget::ENEMY_INITIAL:
|
||||||
if (drag_.index >= 0 && drag_.index < room_->getEnemyManager()->getCount()) {
|
if (drag_.index >= 0 && drag_.index < room_->getEnemyManager()->getCount()) {
|
||||||
highlight_rect = room_->getEnemyManager()->getEnemy(drag_.index)->getRect();
|
highlight_rect = room_->getEnemyManager()->getEnemy(drag_.index)->getRect();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DragTarget::ENEMY_BOUND1:
|
case DragTarget::ENEMY_BOUND1:
|
||||||
case DragTarget::ENEMY_BOUND2:
|
case DragTarget::ENEMY_BOUND2:
|
||||||
highlight_rect = {.x = drag_.snap_x, .y = drag_.snap_y, .w = SZ, .h = SZ};
|
highlight_rect = {.x = drag_.snap_x, .y = drag_.snap_y, .w = SZ, .h = SZ};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DragTarget::ITEM:
|
case DragTarget::ITEM:
|
||||||
if (drag_.index >= 0 && drag_.index < room_->getItemManager()->getCount()) {
|
if (drag_.index >= 0 && drag_.index < room_->getItemManager()->getCount()) {
|
||||||
highlight_rect = room_->getItemManager()->getItem(drag_.index)->getCollider();
|
highlight_rect = room_->getItemManager()->getItem(drag_.index)->getCollider();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DragTarget::NONE:
|
case DragTarget::NONE:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dibujar rectángulo de highlight alrededor de la entidad
|
|
||||||
SDL_FRect border = {
|
SDL_FRect border = {
|
||||||
.x = highlight_rect.x - 1,
|
.x = highlight_rect.x - 1,
|
||||||
.y = highlight_rect.y - 1,
|
.y = highlight_rect.y - 1,
|
||||||
.w = highlight_rect.w + 2,
|
.w = highlight_rect.w + 2,
|
||||||
.h = highlight_rect.h + 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
|
// 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; }
|
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<int>(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<int>(e.vx)) +
|
||||||
|
" vy:" + std::to_string(static_cast<int>(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<int>(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>(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>(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<int>(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<int>(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
|
#endif // _DEBUG
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ class MapEditor {
|
|||||||
void update(float delta_time);
|
void update(float delta_time);
|
||||||
void render();
|
void render();
|
||||||
void handleEvent(const SDL_Event& event);
|
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:
|
private:
|
||||||
static MapEditor* instance_; // [SINGLETON] Objeto privado
|
static MapEditor* instance_; // [SINGLETON] Objeto privado
|
||||||
@@ -38,49 +42,47 @@ class MapEditor {
|
|||||||
MapEditor(); // Constructor
|
MapEditor(); // Constructor
|
||||||
~MapEditor(); // Destructor
|
~MapEditor(); // Destructor
|
||||||
|
|
||||||
// Tipos para drag & drop
|
// Tipos para drag & drop y selección
|
||||||
enum class DragTarget { NONE,
|
enum class DragTarget { NONE, PLAYER, ENEMY_INITIAL, ENEMY_BOUND1, ENEMY_BOUND2, ITEM };
|
||||||
PLAYER,
|
|
||||||
ENEMY_INITIAL,
|
|
||||||
ENEMY_BOUND1,
|
|
||||||
ENEMY_BOUND2,
|
|
||||||
ITEM };
|
|
||||||
|
|
||||||
struct DragState {
|
struct DragState {
|
||||||
DragTarget target{DragTarget::NONE};
|
DragTarget target{DragTarget::NONE};
|
||||||
int index{-1}; // Índice del enemigo o item en room_data_
|
int index{-1};
|
||||||
float offset_x{0.0F}; // Offset entre el cursor y el origen de la entidad al inicio del drag
|
float offset_x{0.0F};
|
||||||
float offset_y{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};
|
float snap_y{0.0F};
|
||||||
|
bool moved{false}; // true si el ratón se movió durante el drag
|
||||||
};
|
};
|
||||||
|
|
||||||
// Métodos internos
|
// Métodos internos
|
||||||
void updateMousePosition(); // Convierte coordenadas de ventana a coordenadas de juego y tile
|
void updateMousePosition();
|
||||||
void renderEnemyBoundaries(); // Dibuja marcadores de boundaries y líneas de ruta
|
void renderEnemyBoundaries();
|
||||||
void renderBoundaryMarker(float x, float y, Uint8 color); // Dibuja un marcador de boundary en una posición
|
void renderBoundaryMarker(float x, float y, Uint8 color);
|
||||||
void renderSelectionHighlight(); // Dibuja highlight del elemento seleccionado/arrastrado
|
void renderSelectionHighlight();
|
||||||
void handleMouseDown(float game_x, float game_y); // Procesa click del ratón (hit test + inicio de drag)
|
void handleMouseDown(float game_x, float game_y);
|
||||||
void handleMouseUp(); // Procesa soltar el ratón (commit del drag + autosave)
|
void handleMouseUp();
|
||||||
void updateDrag(); // Actualiza la posición snapped durante el drag
|
void updateDrag();
|
||||||
void autosave(); // Guarda los cambios puntuales al YAML
|
void autosave();
|
||||||
static auto snapToGrid(float value) -> float; // Alinea un valor a la cuadrícula de 8x8
|
void updateStatusBarInfo();
|
||||||
static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool; // Hit test punto en rectángulo
|
static auto snapToGrid(float value) -> float;
|
||||||
|
static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool;
|
||||||
|
|
||||||
// Estado del editor
|
// Estado del editor
|
||||||
bool active_{false};
|
bool active_{false};
|
||||||
DragState drag_;
|
DragState drag_;
|
||||||
|
int selected_enemy_{-1}; // Índice del enemigo seleccionado (-1 = ninguno)
|
||||||
|
|
||||||
// Datos de la habitación
|
// Datos de la habitación
|
||||||
Room::Data room_data_; // Copia mutable (para boundaries y edición)
|
Room::Data room_data_;
|
||||||
std::string room_path_; // Nombre del fichero (ej: "06.yaml")
|
std::string room_path_;
|
||||||
std::string file_path_; // Ruta completa del fichero en disco
|
std::string file_path_;
|
||||||
|
|
||||||
// YAML: nodo vivo (se edita parcialmente) y backup para revert
|
// YAML: nodo original (para campos que no se editan: name_ca, etc.)
|
||||||
fkyaml::node yaml_; // Nodo YAML actual (editado parcialmente)
|
fkyaml::node yaml_;
|
||||||
fkyaml::node yaml_backup_; // Backup del YAML original al entrar
|
fkyaml::node yaml_backup_;
|
||||||
|
|
||||||
// Referencias a objetos vivos (para rendering)
|
// Referencias a objetos vivos
|
||||||
std::shared_ptr<Room> room_;
|
std::shared_ptr<Room> room_;
|
||||||
std::shared_ptr<Player> player_;
|
std::shared_ptr<Player> player_;
|
||||||
std::shared_ptr<Scoreboard::Data> scoreboard_data_;
|
std::shared_ptr<Scoreboard::Data> scoreboard_data_;
|
||||||
@@ -88,13 +90,13 @@ class MapEditor {
|
|||||||
// Barra de estado del editor
|
// Barra de estado del editor
|
||||||
std::unique_ptr<EditorStatusBar> statusbar_;
|
std::unique_ptr<EditorStatusBar> statusbar_;
|
||||||
|
|
||||||
// Estado del ratón en coordenadas de juego
|
// Estado del ratón
|
||||||
float mouse_game_x_{0.0F};
|
float mouse_game_x_{0.0F};
|
||||||
float mouse_game_y_{0.0F};
|
float mouse_game_y_{0.0F};
|
||||||
int mouse_tile_x_{0};
|
int mouse_tile_x_{0};
|
||||||
int mouse_tile_y_{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};
|
Options::Cheat::State invincible_before_editor_{Options::Cheat::State::DISABLED};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ void Console::redrawText() {
|
|||||||
|
|
||||||
// Línea de input (siempre la última)
|
// Línea de input (siempre la última)
|
||||||
const bool SHOW_CURSOR = cursor_visible_ && (static_cast<int>(input_line_.size()) < MAX_LINE_CHARS);
|
const bool SHOW_CURSOR = cursor_visible_ && (static_cast<int>(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);
|
text_->writeColored(PADDING_IN_H, y_pos, INPUT_STR, BORDER_COLOR);
|
||||||
|
|
||||||
Screen::get()->setRendererSurface(previous_renderer);
|
Screen::get()->setRendererSurface(previous_renderer);
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ class Console {
|
|||||||
auto getVisibleHeight() -> int; // Píxeles visibles actuales (0 = oculta, height_ = totalmente visible)
|
auto getVisibleHeight() -> int; // Píxeles visibles actuales (0 = oculta, height_ = totalmente visible)
|
||||||
[[nodiscard]] auto getText() const -> std::shared_ptr<Text> { return text_; }
|
[[nodiscard]] auto getText() const -> std::shared_ptr<Text> { return text_; }
|
||||||
|
|
||||||
|
// Prompt configurable (por defecto "> ")
|
||||||
|
void setPrompt(const std::string& prompt) { prompt_ = prompt; }
|
||||||
|
|
||||||
// Callback llamado al abrir (true) o cerrar (false) la consola
|
// Callback llamado al abrir (true) o cerrar (false) la consola
|
||||||
std::function<void(bool)> on_toggle;
|
std::function<void(bool)> on_toggle;
|
||||||
|
|
||||||
@@ -84,6 +87,7 @@ class Console {
|
|||||||
// Estado de la entrada de texto
|
// Estado de la entrada de texto
|
||||||
std::vector<std::string> msg_lines_; // Líneas de mensaje (1 o más)
|
std::vector<std::string> msg_lines_; // Líneas de mensaje (1 o más)
|
||||||
std::string input_line_;
|
std::string input_line_;
|
||||||
|
std::string prompt_{"> "}; // Prompt configurable
|
||||||
float cursor_timer_{0.0F};
|
float cursor_timer_{0.0F};
|
||||||
bool cursor_visible_{true};
|
bool cursor_visible_{true};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||||
#include "core/rendering/screen.hpp" // Para Screen
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "core/resources/resource_helper.hpp" // Para Resource::Helper
|
#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 "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||||
#include "game/game_control.hpp" // Para GameControl
|
#include "game/game_control.hpp" // Para GameControl
|
||||||
#include "game/options.hpp" // Para Options
|
#include "game/options.hpp" // Para Options
|
||||||
@@ -703,6 +704,14 @@ static auto cmd_edit(const std::vector<std::string>& args) -> std::string {
|
|||||||
}
|
}
|
||||||
return "usage: edit [on|off|revert]";
|
return "usage: edit [on|off|revert]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SET <property> <value> — modifica propiedad del enemigo seleccionado en el editor
|
||||||
|
static auto cmd_set(const std::vector<std::string>& 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 <animation|color|vx|vy|flip> <value>"; }
|
||||||
|
return MapEditor::get()->setEnemyProperty(args[0], args[1]);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// SHOW [INFO|NOTIFICATION|CHEEVO]
|
// SHOW [INFO|NOTIFICATION|CHEEVO]
|
||||||
@@ -904,6 +913,7 @@ void CommandRegistry::registerHandlers() {
|
|||||||
handlers_["cmd_room"] = cmd_room;
|
handlers_["cmd_room"] = cmd_room;
|
||||||
handlers_["cmd_scene"] = cmd_scene;
|
handlers_["cmd_scene"] = cmd_scene;
|
||||||
handlers_["cmd_edit"] = cmd_edit;
|
handlers_["cmd_edit"] = cmd_edit;
|
||||||
|
handlers_["cmd_set"] = cmd_set;
|
||||||
#endif
|
#endif
|
||||||
// HELP se registra en load() como lambda que captura this
|
// HELP se registra en load() como lambda que captura this
|
||||||
|
|
||||||
@@ -930,6 +940,29 @@ void CommandRegistry::registerHandlers() {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// SET COLOR: colores de la paleta (UPPERCASE, sin .yaml)
|
||||||
|
dynamic_providers_["SET COLOR"] = []() -> std::vector<std::string> {
|
||||||
|
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::string> {
|
||||||
|
std::vector<std::string> 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) {
|
void CommandRegistry::load(const std::string& yaml_path) {
|
||||||
|
|||||||
Reference in New Issue
Block a user