drag'n drop de enemics, boundaries i items
This commit is contained in:
@@ -7,16 +7,18 @@
|
||||
#include <cmath> // Para std::round
|
||||
#include <iostream> // Para cout
|
||||
|
||||
#include "core/input/mouse.hpp" // Para Mouse
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/input/mouse.hpp" // Para Mouse
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource::Cache
|
||||
#include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar
|
||||
#include "game/entities/player.hpp" // Para Player
|
||||
#include "game/gameplay/room.hpp" // Para Room
|
||||
#include "game/options.hpp" // Para Options
|
||||
#include "utils/defines.hpp" // Para Tile::SIZE, PlayArea
|
||||
#include "utils/utils.hpp" // Para stringToColor
|
||||
#include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar
|
||||
#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 "utils/defines.hpp" // Para Tile::SIZE, PlayArea
|
||||
#include "utils/utils.hpp" // Para stringToColor
|
||||
|
||||
// Singleton
|
||||
MapEditor* MapEditor::instance_ = nullptr;
|
||||
@@ -41,8 +43,7 @@ MapEditor::MapEditor() = default;
|
||||
MapEditor::~MapEditor() = default;
|
||||
|
||||
// Entra en modo editor
|
||||
void MapEditor::enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player,
|
||||
const std::string& room_path, std::shared_ptr<Scoreboard::Data> scoreboard_data) {
|
||||
void MapEditor::enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player, const std::string& room_path, std::shared_ptr<Scoreboard::Data> scoreboard_data) {
|
||||
if (active_) { return; }
|
||||
|
||||
room_ = std::move(room);
|
||||
@@ -61,6 +62,9 @@ void MapEditor::enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player
|
||||
Options::cheats.invincible = Options::Cheat::State::ENABLED;
|
||||
player_->setColor();
|
||||
|
||||
// Resetear enemigos a su posición inicial (pueden haberse movido durante el gameplay)
|
||||
room_->resetEnemyPositions(room_data_.enemies);
|
||||
|
||||
// Crear la barra de estado
|
||||
statusbar_ = std::make_unique<EditorStatusBar>(room_->getNumber(), room_->getName());
|
||||
|
||||
@@ -108,9 +112,34 @@ void MapEditor::update(float delta_time) {
|
||||
updateDrag();
|
||||
}
|
||||
|
||||
// Actualizar la barra de estado con las coordenadas del ratón
|
||||
// Actualizar la barra de estado con las coordenadas del ratón y la selección
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -147,34 +176,108 @@ void MapEditor::handleEvent(const SDL_Event& event) {
|
||||
|
||||
// Procesa click del ratón: hit test + inicio de drag
|
||||
void MapEditor::handleMouseDown(float game_x, float game_y) {
|
||||
// Prioridad de hit test: jugador → enemigos → items
|
||||
// Prioridad de hit test: jugador → enemigos (initial) → enemigos (boundaries) → items
|
||||
|
||||
// 1. Hit test sobre el jugador
|
||||
// Helper para iniciar drag
|
||||
auto startDrag = [&](DragTarget target, int index, float entity_x, float entity_y) {
|
||||
drag_.target = target;
|
||||
drag_.index = index;
|
||||
drag_.offset_x = game_x - entity_x;
|
||||
drag_.offset_y = game_y - entity_y;
|
||||
drag_.snap_x = entity_x;
|
||||
drag_.snap_y = entity_y;
|
||||
};
|
||||
|
||||
// 1. Hit test sobre el jugador (8x16)
|
||||
SDL_FRect player_rect = player_->getRect();
|
||||
if (pointInRect(game_x, game_y, player_rect)) {
|
||||
drag_.target = DragTarget::PLAYER;
|
||||
drag_.index = -1;
|
||||
drag_.offset_x = game_x - player_rect.x;
|
||||
drag_.offset_y = game_y - player_rect.y;
|
||||
drag_.snap_x = player_rect.x;
|
||||
drag_.snap_y = player_rect.y;
|
||||
startDrag(DragTarget::PLAYER, -1, player_rect.x, player_rect.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// (Fases 4+: hit test sobre enemigos e items)
|
||||
// 2. Hit test sobre enemigos: posición inicial (usan el rect del sprite vivo)
|
||||
auto* enemy_mgr = room_->getEnemyManager();
|
||||
for (int i = 0; i < enemy_mgr->getCount(); ++i) {
|
||||
SDL_FRect enemy_rect = enemy_mgr->getEnemy(i)->getRect();
|
||||
if (pointInRect(game_x, game_y, enemy_rect)) {
|
||||
startDrag(DragTarget::ENEMY_INITIAL, i, enemy_rect.x, enemy_rect.y);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Hit test sobre boundaries de enemigos (rectángulos 8x8 en las posiciones de room_data_)
|
||||
for (int i = 0; i < static_cast<int>(room_data_.enemies.size()); ++i) {
|
||||
const auto& ed = room_data_.enemies[i];
|
||||
constexpr float SZ = static_cast<float>(Tile::SIZE);
|
||||
|
||||
SDL_FRect b1_rect = {.x = static_cast<float>(ed.x1), .y = static_cast<float>(ed.y1), .w = SZ, .h = SZ};
|
||||
if (pointInRect(game_x, game_y, b1_rect)) {
|
||||
startDrag(DragTarget::ENEMY_BOUND1, i, b1_rect.x, b1_rect.y);
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_FRect b2_rect = {.x = static_cast<float>(ed.x2), .y = static_cast<float>(ed.y2), .w = SZ, .h = SZ};
|
||||
if (pointInRect(game_x, game_y, b2_rect)) {
|
||||
startDrag(DragTarget::ENEMY_BOUND2, i, b2_rect.x, b2_rect.y);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Hit test sobre items (8x8)
|
||||
auto* item_mgr = room_->getItemManager();
|
||||
for (int i = 0; i < item_mgr->getCount(); ++i) {
|
||||
SDL_FRect item_rect = item_mgr->getItem(i)->getCollider();
|
||||
if (pointInRect(game_x, game_y, item_rect)) {
|
||||
startDrag(DragTarget::ITEM, i, item_rect.x, item_rect.y);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Procesa soltar el ratón: commit del drag
|
||||
void MapEditor::handleMouseUp() {
|
||||
if (drag_.target == DragTarget::NONE) { return; }
|
||||
|
||||
const int IDX = drag_.index;
|
||||
const int SNAP_X = static_cast<int>(drag_.snap_x);
|
||||
const int SNAP_Y = static_cast<int>(drag_.snap_y);
|
||||
|
||||
switch (drag_.target) {
|
||||
case DragTarget::PLAYER:
|
||||
// Mover el jugador a la posición snapped
|
||||
player_->setDebugPosition(drag_.snap_x, drag_.snap_y);
|
||||
player_->finalizeDebugTeleport();
|
||||
break;
|
||||
default:
|
||||
|
||||
case DragTarget::ENEMY_INITIAL:
|
||||
// Actualizar datos mutables y posición del sprite vivo
|
||||
if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
|
||||
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]);
|
||||
}
|
||||
break;
|
||||
|
||||
case DragTarget::ENEMY_BOUND1:
|
||||
if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
|
||||
room_data_.enemies[IDX].x1 = SNAP_X;
|
||||
room_data_.enemies[IDX].y1 = SNAP_Y;
|
||||
}
|
||||
break;
|
||||
|
||||
case DragTarget::ENEMY_BOUND2:
|
||||
if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
|
||||
room_data_.enemies[IDX].x2 = SNAP_X;
|
||||
room_data_.enemies[IDX].y2 = SNAP_Y;
|
||||
}
|
||||
break;
|
||||
|
||||
case DragTarget::ITEM:
|
||||
if (IDX >= 0 && IDX < room_->getItemManager()->getCount()) {
|
||||
room_->getItemManager()->getItem(IDX)->setPosition(drag_.snap_x, drag_.snap_y);
|
||||
}
|
||||
break;
|
||||
|
||||
case DragTarget::NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -191,8 +294,37 @@ void MapEditor::updateDrag() {
|
||||
drag_.snap_y = snapToGrid(raw_y);
|
||||
|
||||
// Mientras arrastramos, mover la entidad visualmente a la posición snapped
|
||||
if (drag_.target == DragTarget::PLAYER) {
|
||||
player_->setDebugPosition(drag_.snap_x, drag_.snap_y);
|
||||
switch (drag_.target) {
|
||||
case DragTarget::PLAYER:
|
||||
player_->setDebugPosition(drag_.snap_x, drag_.snap_y);
|
||||
break;
|
||||
|
||||
case DragTarget::ENEMY_INITIAL:
|
||||
if (drag_.index >= 0 && drag_.index < room_->getEnemyManager()->getCount()) {
|
||||
// Mover el sprite vivo del enemigo durante el arrastre
|
||||
auto& enemy = room_->getEnemyManager()->getEnemy(drag_.index);
|
||||
Enemy::Data temp_data = room_data_.enemies[drag_.index];
|
||||
temp_data.x = drag_.snap_x;
|
||||
temp_data.y = drag_.snap_y;
|
||||
enemy->resetToInitialPosition(temp_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case DragTarget::ENEMY_BOUND1:
|
||||
// Los boundaries se actualizan visualmente en renderEnemyBoundaries() via drag_.snap
|
||||
break;
|
||||
|
||||
case DragTarget::ENEMY_BOUND2:
|
||||
break;
|
||||
|
||||
case DragTarget::ITEM:
|
||||
if (drag_.index >= 0 && drag_.index < room_->getItemManager()->getCount()) {
|
||||
room_->getItemManager()->getItem(drag_.index)->setPosition(drag_.snap_x, drag_.snap_y);
|
||||
}
|
||||
break;
|
||||
|
||||
case DragTarget::NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,13 +336,32 @@ void MapEditor::renderSelectionHighlight() {
|
||||
if (!game_surface) { return; }
|
||||
|
||||
const Uint8 HIGHLIGHT_COLOR = stringToColor("bright_white");
|
||||
constexpr float SZ = static_cast<float>(Tile::SIZE);
|
||||
|
||||
SDL_FRect highlight_rect{};
|
||||
switch (drag_.target) {
|
||||
case DragTarget::PLAYER:
|
||||
highlight_rect = player_->getRect();
|
||||
break;
|
||||
default:
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -219,8 +370,7 @@ void MapEditor::renderSelectionHighlight() {
|
||||
.x = highlight_rect.x - 1,
|
||||
.y = highlight_rect.y - 1,
|
||||
.w = highlight_rect.w + 2,
|
||||
.h = highlight_rect.h + 2
|
||||
};
|
||||
.h = highlight_rect.h + 2};
|
||||
game_surface->drawRectBorder(&border, HIGHLIGHT_COLOR);
|
||||
}
|
||||
|
||||
@@ -243,25 +393,39 @@ void MapEditor::renderEnemyBoundaries() {
|
||||
const Uint8 COLOR_BOUND2 = stringToColor("bright_yellow");
|
||||
const Uint8 COLOR_ROUTE = stringToColor("bright_white");
|
||||
|
||||
for (const auto& enemy : room_data_.enemies) {
|
||||
// Dibujar línea de ruta: boundary1 → posición inicial → boundary2
|
||||
for (int i = 0; i < static_cast<int>(room_data_.enemies.size()); ++i) {
|
||||
const auto& enemy = room_data_.enemies[i];
|
||||
constexpr float HALF = Tile::SIZE / 2.0F;
|
||||
|
||||
float init_cx = enemy.x + HALF;
|
||||
float init_cy = enemy.y + HALF;
|
||||
float b1_cx = static_cast<float>(enemy.x1) + HALF;
|
||||
float b1_cy = static_cast<float>(enemy.y1) + HALF;
|
||||
float b2_cx = static_cast<float>(enemy.x2) + HALF;
|
||||
float b2_cy = static_cast<float>(enemy.y2) + HALF;
|
||||
// Posiciones base (pueden estar siendo arrastradas)
|
||||
float init_x = enemy.x;
|
||||
float init_y = enemy.y;
|
||||
float b1_x = static_cast<float>(enemy.x1);
|
||||
float b1_y = static_cast<float>(enemy.y1);
|
||||
float b2_x = static_cast<float>(enemy.x2);
|
||||
float b2_y = static_cast<float>(enemy.y2);
|
||||
|
||||
// Línea de boundary1 a posición inicial
|
||||
game_surface->drawLine(b1_cx, b1_cy, init_cx, init_cy, COLOR_ROUTE);
|
||||
// Línea de posición inicial a boundary2
|
||||
game_surface->drawLine(init_cx, init_cy, b2_cx, b2_cy, COLOR_ROUTE);
|
||||
// Si estamos arrastrando una boundary de este enemigo, usar la posición snapped
|
||||
if (drag_.index == i) {
|
||||
if (drag_.target == DragTarget::ENEMY_BOUND1) {
|
||||
b1_x = drag_.snap_x;
|
||||
b1_y = drag_.snap_y;
|
||||
} else if (drag_.target == DragTarget::ENEMY_BOUND2) {
|
||||
b2_x = drag_.snap_x;
|
||||
b2_y = drag_.snap_y;
|
||||
} else if (drag_.target == DragTarget::ENEMY_INITIAL) {
|
||||
init_x = drag_.snap_x;
|
||||
init_y = drag_.snap_y;
|
||||
}
|
||||
}
|
||||
|
||||
// Dibujar líneas de ruta
|
||||
game_surface->drawLine(b1_x + HALF, b1_y + HALF, init_x + HALF, init_y + HALF, COLOR_ROUTE);
|
||||
game_surface->drawLine(init_x + HALF, init_y + HALF, b2_x + HALF, b2_y + HALF, COLOR_ROUTE);
|
||||
|
||||
// Marcadores en las boundaries
|
||||
renderBoundaryMarker(static_cast<float>(enemy.x1), static_cast<float>(enemy.y1), COLOR_BOUND1);
|
||||
renderBoundaryMarker(static_cast<float>(enemy.x2), static_cast<float>(enemy.y2), COLOR_BOUND2);
|
||||
renderBoundaryMarker(b1_x, b1_y, COLOR_BOUND1);
|
||||
renderBoundaryMarker(b2_x, b2_y, COLOR_BOUND2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user