treballant en editor de items i tile_picker

This commit is contained in:
2026-04-02 14:49:26 +02:00
parent acaf434e5c
commit 22d6ac2fbf
15 changed files with 540 additions and 45 deletions

View File

@@ -13,13 +13,13 @@
#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
#include "game/gameplay/item_manager.hpp" // Para ItemManager
#include "game/gameplay/room.hpp" // Para Room
#include "game/options.hpp" // Para Options
#include "game/ui/console.hpp" // Para Console
#include "utils/defines.hpp" // Para Tile::SIZE, PlayArea
#include "utils/utils.hpp" // Para stringToColor
@@ -193,6 +193,11 @@ void MapEditor::render() {
// Renderizar highlight de selección (encima de los sprites)
renderSelectionHighlight();
// Tile picker (encima de todo en el play area)
if (tile_picker_.isOpen()) {
tile_picker_.render();
}
// Renderizar barra de estado del editor (reemplaza al scoreboard)
if (statusbar_) {
statusbar_->render();
@@ -201,6 +206,12 @@ void MapEditor::render() {
// Maneja eventos del editor
void MapEditor::handleEvent(const SDL_Event& event) {
// Si el tile picker está abierto, los eventos van a él
if (tile_picker_.isOpen()) {
tile_picker_.handleEvent(event);
return;
}
if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) {
handleMouseDown(mouse_game_x_, mouse_game_y_);
} else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) {
@@ -268,8 +279,9 @@ void MapEditor::handleMouseDown(float game_x, float game_y) {
}
}
// Click en el fondo: deseleccionar
// Click en el fondo: deseleccionar todo
selected_enemy_ = -1;
selected_item_ = -1;
}
// Procesa soltar el ratón: commit del drag
@@ -281,11 +293,14 @@ void MapEditor::handleMouseUp() {
// 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_item_ = -1;
} else if (drag_.target == DragTarget::ITEM) {
selected_item_ = (selected_item_ == IDX) ? -1 : IDX;
selected_enemy_ = -1;
} else {
selected_enemy_ = -1;
selected_item_ = -1;
}
drag_ = {};
return;
@@ -333,6 +348,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_item_ = IDX;
selected_enemy_ = -1;
changed = true;
}
@@ -415,6 +431,17 @@ void MapEditor::renderSelectionHighlight() {
game_surface->drawRectBorder(&border, stringToColor("bright_green"));
}
// Highlight del item seleccionado (persistente, color bright_green)
if (selected_item_ >= 0 && selected_item_ < room_->getItemManager()->getCount()) {
SDL_FRect item_rect = room_->getItemManager()->getItem(selected_item_)->getCollider();
SDL_FRect border = {
.x = item_rect.x - 1,
.y = item_rect.y - 1,
.w = item_rect.w + 2,
.h = item_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; }
@@ -559,12 +586,23 @@ void MapEditor::updateStatusBarInfo() {
// Info de drag activo (línea 5, junto a tile coords)
if (drag_.target != DragTarget::NONE && drag_.moved) {
switch (drag_.target) {
case DragTarget::PLAYER: line5 = "dragging: player"; break;
case DragTarget::ENEMY_INITIAL: line5 = "dragging: enemy " + std::to_string(drag_.index); break;
case DragTarget::ENEMY_BOUND1: line5 = "dragging: e" + std::to_string(drag_.index) + " bound1"; break;
case DragTarget::ENEMY_BOUND2: line5 = "dragging: e" + std::to_string(drag_.index) + " bound2"; break;
case DragTarget::ITEM: line5 = "dragging: item " + std::to_string(drag_.index); break;
case DragTarget::NONE: break;
case DragTarget::PLAYER:
line5 = "dragging: player";
break;
case DragTarget::ENEMY_INITIAL:
line5 = "dragging: enemy " + std::to_string(drag_.index);
break;
case DragTarget::ENEMY_BOUND1:
line5 = "dragging: e" + std::to_string(drag_.index) + " bound1";
break;
case DragTarget::ENEMY_BOUND2:
line5 = "dragging: e" + std::to_string(drag_.index) + " bound2";
break;
case DragTarget::ITEM:
line5 = "dragging: item " + std::to_string(drag_.index);
break;
case DragTarget::NONE:
break;
}
}
@@ -578,29 +616,40 @@ void MapEditor::updateStatusBarInfo() {
line2 = "enemy " + std::to_string(selected_enemy_) + ": " + anim + " " + e.color;
line3 = "vx:" + std::to_string(static_cast<int>(e.vx)) +
" vy:" + std::to_string(static_cast<int>(e.vy));
" vy:" + std::to_string(static_cast<int>(e.vy));
if (e.flip) { line3 += " flip"; }
if (e.mirror) { line3 += " mirror"; }
} else if (selected_item_ >= 0 && selected_item_ < static_cast<int>(room_data_.items.size())) {
// Item seleccionado
const auto& it = room_data_.items[selected_item_];
line2 = "item " + std::to_string(selected_item_) + ": tile=" + std::to_string(it.tile) +
" counter=" + std::to_string(it.counter);
line3 = "tileset: " + it.tile_set_file;
} else {
// Propiedades de la habitación
std::string conv = "none";
if (room_data_.conveyor_belt_direction < 0) { conv = "left"; }
else if (room_data_.conveyor_belt_direction > 0) { conv = "right"; }
if (room_data_.conveyor_belt_direction < 0) {
conv = "left";
} else if (room_data_.conveyor_belt_direction > 0) {
conv = "right";
}
line2 = "bg:" + room_data_.bg_color + " brd:" + room_data_.border_color + " conv:" + conv;
line3 = "u:" + conn(room_data_.upper_room) + " d:" + conn(room_data_.lower_room) +
" l:" + conn(room_data_.left_room) + " r:" + conn(room_data_.right_room) +
" itm:" + room_data_.item_color1 + "/" + room_data_.item_color2;
" l:" + conn(room_data_.left_room) + " r:" + conn(room_data_.right_room) +
" itm:" + room_data_.item_color1 + "/" + room_data_.item_color2;
}
statusbar_->setLine2(line2);
statusbar_->setLine3(line3);
statusbar_->setLine4(""); // Disponible para uso futuro
statusbar_->setLine4("");
statusbar_->setLine5(line5);
// 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 if (selected_item_ >= 0) {
Console::get()->setPrompt("item " + std::to_string(selected_item_) + "> ");
} else {
Console::get()->setPrompt("room> ");
}
@@ -826,9 +875,14 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
}
if (property == "CONVEYOR") {
if (val == "left") { room_data_.conveyor_belt_direction = -1; }
else if (val == "right") { room_data_.conveyor_belt_direction = 1; }
else { room_data_.conveyor_belt_direction = 0; val = "none"; }
if (val == "left") {
room_data_.conveyor_belt_direction = -1;
} else if (val == "right") {
room_data_.conveyor_belt_direction = 1;
} else {
room_data_.conveyor_belt_direction = 0;
val = "none";
}
autosave();
return "conveyor: " + val;
}
@@ -858,10 +912,15 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
}
}
if (property == "UP") { room_data_.upper_room = connection; }
else if (property == "DOWN") { room_data_.lower_room = connection; }
else if (property == "LEFT") { room_data_.left_room = connection; }
else { room_data_.right_room = connection; }
if (property == "UP") {
room_data_.upper_room = connection;
} else if (property == "DOWN") {
room_data_.lower_room = connection;
} else if (property == "LEFT") {
room_data_.left_room = connection;
} else {
room_data_.right_room = connection;
}
autosave();
return toLower(property) + ": " + connection;
@@ -870,4 +929,113 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
return "Unknown property: " + property + " (use: bgcolor, border, itemcolor1, itemcolor2, conveyor, tileset, up, down, left, right)";
}
// ¿Hay un item seleccionado?
auto MapEditor::hasSelectedItem() const -> bool {
return selected_item_ >= 0 && selected_item_ < static_cast<int>(room_data_.items.size());
}
// Modifica una propiedad del item seleccionado
auto MapEditor::setItemProperty(const std::string& property, const std::string& value) -> std::string {
if (!active_) { return "Editor not active"; }
if (!hasSelectedItem()) { return "No item selected"; }
auto& item = room_data_.items[selected_item_];
if (property == "TILE") {
// Abrir el tile picker visual
openTilePicker(item.tile_set_file, item.tile);
return "Select tile...";
}
if (property == "COUNTER") {
try {
item.counter = std::stoi(value);
} catch (...) { return "Invalid value: " + value; }
autosave();
return "counter: " + std::to_string(item.counter);
}
return "Unknown property: " + property + " (use: tile, counter)";
}
// Abre el tile picker para seleccionar un tile
void MapEditor::openTilePicker(const std::string& tileset_name, int current_tile) {
// Cerrar la consola si está abierta (para que el primer click vaya al picker)
if (Console::get()->isActive()) {
Console::get()->toggle();
}
tile_picker_.on_select = [this](int tile) {
if (!hasSelectedItem()) { return; }
room_data_.items[selected_item_].tile = tile;
room_->getItemManager()->getItem(selected_item_)->setTile(tile);
autosave();
};
// Pasar color de fondo de la habitación + color de sustitución del item
int bg = stringToColor(room_data_.bg_color);
int item_color = stringToColor(room_data_.item_color1);
tile_picker_.open(tileset_name, current_tile, bg, 1, item_color);
}
// Crea un nuevo item con valores por defecto, centrado en la habitación
auto MapEditor::addItem() -> std::string {
if (!active_) { return "Editor not active"; }
Item::Data new_item;
new_item.tile_set_file = "items.gif";
new_item.tile = 42; // Tile por defecto
new_item.x = PlayArea::CENTER_X;
new_item.y = PlayArea::CENTER_Y;
new_item.counter = 0;
new_item.color1 = stringToColor(room_data_.item_color1);
new_item.color2 = stringToColor(room_data_.item_color2);
room_data_.items.push_back(new_item);
room_->getItemManager()->addItem(std::make_shared<Item>(new_item));
selected_item_ = static_cast<int>(room_data_.items.size()) - 1;
selected_enemy_ = -1;
autosave();
return "Added item " + std::to_string(selected_item_);
}
// Elimina el item seleccionado
auto MapEditor::deleteItem() -> std::string {
if (!active_) { return "Editor not active"; }
if (!hasSelectedItem()) { return "No item selected"; }
const int IDX = selected_item_;
room_data_.items.erase(room_data_.items.begin() + IDX);
// Recrear todos los items (los índices cambian al borrar)
auto* item_mgr = room_->getItemManager();
item_mgr->clear();
for (const auto& item_data : room_data_.items) {
item_mgr->addItem(std::make_shared<Item>(item_data));
}
selected_item_ = -1;
autosave();
return "Deleted item " + std::to_string(IDX);
}
// Duplica el item seleccionado (lo pone un tile a la derecha)
auto MapEditor::duplicateItem() -> std::string {
if (!active_) { return "Editor not active"; }
if (!hasSelectedItem()) { return "No item selected"; }
Item::Data copy = room_data_.items[selected_item_];
copy.x += Tile::SIZE;
room_data_.items.push_back(copy);
room_->getItemManager()->addItem(std::make_shared<Item>(copy));
selected_item_ = static_cast<int>(room_data_.items.size()) - 1;
selected_enemy_ = -1;
autosave();
return "Duplicated as item " + std::to_string(selected_item_);
}
#endif // _DEBUG