fixos en la plataforma mobil

This commit is contained in:
2026-04-08 17:39:19 +02:00
parent 3db74ebd4d
commit 88a822c562
8 changed files with 395 additions and 240 deletions

View File

@@ -330,13 +330,7 @@ void MapEditor::autosave() {
room_data_.items[i].y = pos.y;
}
// Sincronizar posiciones de plataformas desde los sprites vivos a room_data_
auto* platform_mgr = room_->getPlatformManager();
for (int i = 0; i < platform_mgr->getCount() && i < static_cast<int>(room_data_.platforms.size()); ++i) {
SDL_FRect rect = platform_mgr->getPlatform(i)->getRect();
room_data_.platforms[i].x = rect.x;
room_data_.platforms[i].y = rect.y;
}
// Platforms are already synced via resetToInitialPosition during drag commit
RoomSaver::saveYAML(file_path_, yaml_, room_data_);
}
@@ -627,8 +621,8 @@ void MapEditor::handleMouseDown(float game_x, float game_y) {
}
}
// 3. Hit test on boundaries (enemies and platforms)
for (auto type : {EntityType::ENEMY, EntityType::PLATFORM}) {
// 3. Hit test on boundaries (enemies only)
for (auto type : {EntityType::ENEMY}) {
for (int i = 0; i < entityDataCount(type); ++i) {
auto bd = entityBoundaries(type, i);
constexpr auto SZ = static_cast<float>(Tile::SIZE);
@@ -718,9 +712,16 @@ auto MapEditor::commitEntityDrag() -> bool {
break;
case EntityType::PLATFORM:
if (IDX >= 0 && IDX < static_cast<int>(room_data_.platforms.size())) {
room_data_.platforms[IDX].x = drag_.snap_x;
room_data_.platforms[IDX].y = drag_.snap_y;
room_->getPlatformManager()->getPlatform(IDX)->resetToInitialPosition(room_data_.platforms[IDX]);
auto& plat = room_data_.platforms[IDX];
if (!plat.path.empty()) {
float dx = drag_.snap_x - plat.path[0].x;
float dy = drag_.snap_y - plat.path[0].y;
for (auto& wp : plat.path) {
wp.x += dx;
wp.y += dy;
}
}
room_->getPlatformManager()->getPlatform(IDX)->resetToInitialPosition(plat);
selection_ = {EntityType::PLATFORM, IDX};
return true;
}
@@ -741,15 +742,6 @@ auto MapEditor::commitEntityDrag() -> bool {
return true;
}
break;
case EntityType::PLATFORM:
if (IDX >= 0 && IDX < static_cast<int>(room_data_.platforms.size())) {
room_data_.platforms[IDX].x1 = SNAP_X;
room_data_.platforms[IDX].y1 = SNAP_Y;
room_->getPlatformManager()->getPlatform(IDX)->resetToInitialPosition(room_data_.platforms[IDX]);
selection_ = {EntityType::PLATFORM, IDX};
return true;
}
break;
default:
break;
}
@@ -766,15 +758,6 @@ auto MapEditor::commitEntityDrag() -> bool {
return true;
}
break;
case EntityType::PLATFORM:
if (IDX >= 0 && IDX < static_cast<int>(room_data_.platforms.size())) {
room_data_.platforms[IDX].x2 = SNAP_X;
room_data_.platforms[IDX].y2 = SNAP_Y;
room_->getPlatformManager()->getPlatform(IDX)->resetToInitialPosition(room_data_.platforms[IDX]);
selection_ = {EntityType::PLATFORM, IDX};
return true;
}
break;
default:
break;
}
@@ -807,8 +790,14 @@ void MapEditor::moveEntityVisual() {
case EntityType::PLATFORM:
if (drag_.index >= 0 && drag_.index < room_->getPlatformManager()->getCount()) {
MovingPlatform::Data temp_data = room_data_.platforms[drag_.index];
temp_data.x = drag_.snap_x;
temp_data.y = drag_.snap_y;
if (!temp_data.path.empty()) {
float dx = drag_.snap_x - temp_data.path[0].x;
float dy = drag_.snap_y - temp_data.path[0].y;
for (auto& wp : temp_data.path) {
wp.x += dx;
wp.y += dy;
}
}
room_->getPlatformManager()->getPlatform(drag_.index)->resetToInitialPosition(temp_data);
}
break;
@@ -940,7 +929,7 @@ auto MapEditor::entityRect(EntityType type, int index) -> SDL_FRect {
}
auto MapEditor::entityHasBoundaries(EntityType type) -> bool {
return type == EntityType::ENEMY || type == EntityType::PLATFORM;
return type == EntityType::ENEMY;
}
auto MapEditor::entityBoundaries(EntityType type, int index) const -> BoundaryData {
@@ -949,10 +938,6 @@ auto MapEditor::entityBoundaries(EntityType type, int index) const -> BoundaryDa
const auto& e = room_data_.enemies[index];
return {e.x1, e.y1, e.x2, e.y2};
}
case EntityType::PLATFORM: {
const auto& p = room_data_.platforms[index];
return {p.x1, p.y1, p.x2, p.y2};
}
default: return {};
}
}
@@ -961,7 +946,10 @@ auto MapEditor::entityPosition(EntityType type, int index) const -> std::pair<fl
switch (type) {
case EntityType::ENEMY: return {room_data_.enemies[index].x, room_data_.enemies[index].y};
case EntityType::ITEM: return {room_data_.items[index].x, room_data_.items[index].y};
case EntityType::PLATFORM: return {room_data_.platforms[index].x, room_data_.platforms[index].y};
case EntityType::PLATFORM: {
const auto& path = room_data_.platforms[index].path;
return path.empty() ? std::pair{0.0F, 0.0F} : std::pair{path[0].x, path[0].y};
}
default: return {0.0F, 0.0F};
}
}
@@ -999,48 +987,93 @@ void MapEditor::renderEntityBoundaries() {
constexpr Uint8 DIM_BOUND2 = 13; // YELLOW
constexpr Uint8 DIM_ROUTE = 14; // WHITE (gris medio)
for (auto type : {EntityType::ENEMY, EntityType::PLATFORM}) {
for (int i = 0; i < entityDataCount(type); ++i) {
auto [pos_x, pos_y] = entityPosition(type, i);
auto bd = entityBoundaries(type, i);
constexpr float HALF = Tile::SIZE / 2.0F;
constexpr float HALF = Tile::SIZE / 2.0F;
bool is_selected = selection_.is(type) && selection_.index == i;
for (int i = 0; i < entityDataCount(EntityType::ENEMY); ++i) {
auto [pos_x, pos_y] = entityPosition(EntityType::ENEMY, i);
auto bd = entityBoundaries(EntityType::ENEMY, i);
// Posiciones base (pueden estar siendo arrastradas)
float init_x = pos_x;
float init_y = pos_y;
auto b1_x = static_cast<float>(bd.x1);
auto b1_y = static_cast<float>(bd.y1);
auto b2_x = static_cast<float>(bd.x2);
auto b2_y = static_cast<float>(bd.y2);
bool is_selected = selection_.is(EntityType::ENEMY) && selection_.index == i;
// Si estamos arrastrando una boundary de esta entidad, usar la posición snapped
if (drag_.entity_type == type && drag_.index == i) {
if (drag_.target == DragTarget::ENTITY_BOUND1) {
b1_x = drag_.snap_x;
b1_y = drag_.snap_y;
} else if (drag_.target == DragTarget::ENTITY_BOUND2) {
b2_x = drag_.snap_x;
b2_y = drag_.snap_y;
} else if (drag_.target == DragTarget::ENTITY_INITIAL) {
init_x = drag_.snap_x;
init_y = drag_.snap_y;
}
is_selected = true; // Arrastrando = siempre iluminado
// Posiciones base (pueden estar siendo arrastradas)
float init_x = pos_x;
float init_y = pos_y;
auto b1_x = static_cast<float>(bd.x1);
auto b1_y = static_cast<float>(bd.y1);
auto b2_x = static_cast<float>(bd.x2);
auto b2_y = static_cast<float>(bd.y2);
// Si estamos arrastrando una boundary de esta entidad, usar la posición snapped
if (drag_.entity_type == EntityType::ENEMY && drag_.index == i) {
if (drag_.target == DragTarget::ENTITY_BOUND1) {
b1_x = drag_.snap_x;
b1_y = drag_.snap_y;
} else if (drag_.target == DragTarget::ENTITY_BOUND2) {
b2_x = drag_.snap_x;
b2_y = drag_.snap_y;
} else if (drag_.target == DragTarget::ENTITY_INITIAL) {
init_x = drag_.snap_x;
init_y = drag_.snap_y;
}
is_selected = true; // Arrastrando = siempre iluminado
}
Uint8 color_b1 = is_selected ? SEL_BOUND1 : DIM_BOUND1;
Uint8 color_b2 = is_selected ? SEL_BOUND2 : DIM_BOUND2;
Uint8 color_route = is_selected ? SEL_ROUTE : DIM_ROUTE;
Uint8 color_b1 = is_selected ? SEL_BOUND1 : DIM_BOUND1;
Uint8 color_b2 = is_selected ? SEL_BOUND2 : DIM_BOUND2;
Uint8 color_route = is_selected ? SEL_ROUTE : DIM_ROUTE;
// 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);
// 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(b1_x, b1_y, color_b1);
renderBoundaryMarker(b2_x, b2_y, color_b2);
// Marcadores en las boundaries
renderBoundaryMarker(b1_x, b1_y, color_b1);
renderBoundaryMarker(b2_x, b2_y, color_b2);
}
// Render platform waypoint routes
for (int i = 0; i < entityDataCount(EntityType::PLATFORM); ++i) {
const auto& plat = room_data_.platforms[i];
if (plat.path.size() < 2) { continue; }
bool is_selected = selection_.is(EntityType::PLATFORM) && selection_.index == i;
// If dragging this platform, apply the drag offset to all points for visualization
float drag_dx = 0.0F;
float drag_dy = 0.0F;
if (drag_.entity_type == EntityType::PLATFORM && drag_.index == i && drag_.target == DragTarget::ENTITY_INITIAL) {
if (!plat.path.empty()) {
drag_dx = drag_.snap_x - plat.path[0].x;
drag_dy = drag_.snap_y - plat.path[0].y;
}
is_selected = true;
}
Uint8 color_wp = is_selected ? SEL_BOUND1 : DIM_BOUND1;
Uint8 color_route = is_selected ? SEL_ROUTE : DIM_ROUTE;
// Draw route lines between consecutive waypoints
for (int j = 0; j < static_cast<int>(plat.path.size()) - 1; ++j) {
float ax = plat.path[j].x + drag_dx + HALF;
float ay = plat.path[j].y + drag_dy + HALF;
float bx = plat.path[j + 1].x + drag_dx + HALF;
float by = plat.path[j + 1].y + drag_dy + HALF;
game_surface->drawLine(ax, ay, bx, by, color_route);
}
// For circular mode, draw line from last to first
if (plat.loop == LoopMode::CIRCULAR && plat.path.size() > 2) {
int last = static_cast<int>(plat.path.size()) - 1;
float ax = plat.path[last].x + drag_dx + HALF;
float ay = plat.path[last].y + drag_dy + HALF;
float bx = plat.path[0].x + drag_dx + HALF;
float by = plat.path[0].y + drag_dy + HALF;
game_surface->drawLine(ax, ay, bx, by, color_route);
}
// Draw waypoint markers
for (const auto& wp : plat.path) {
renderBoundaryMarker(wp.x + drag_dx, wp.y + drag_dy, color_wp);
}
}
}
@@ -1145,12 +1178,10 @@ void MapEditor::updateStatusBarInfo() { // NOLINT(readability-function-cognitiv
if (selection_.index < static_cast<int>(room_data_.platforms.size())) {
const auto& p = room_data_.platforms[selection_.index];
std::string anim = p.animation_path;
auto dot = anim.rfind('.');
if (dot != std::string::npos) { anim = anim.substr(0, dot); }
if (anim.size() > 5 && anim.substr(anim.size() - 5) == ".yaml") { anim = anim.substr(0, anim.size() - 5); }
line2 = "platform " + std::to_string(selection_.index) + ": " + anim;
line3 = "vx:" + std::to_string(static_cast<int>(p.vx)) +
" vy:" + std::to_string(static_cast<int>(p.vy));
line3 = "speed:" + std::to_string(static_cast<int>(p.speed)) + " " + (p.loop == LoopMode::CIRCULAR ? "circular" : "pingpong");
if (p.easing != "linear") { line3 += " " + p.easing; }
}
break;
@@ -1197,7 +1228,7 @@ auto MapEditor::getSetCompletions() const -> std::vector<std::string> {
switch (selection_.type) {
case EntityType::ENEMY: return {"ANIMATION", "VX", "VY", "FLIP", "MIRROR"};
case EntityType::ITEM: return {"TILE", "COUNTER"};
case EntityType::PLATFORM: return {"ANIMATION", "VX", "VY"};
case EntityType::PLATFORM: return {"ANIMATION", "SPEED", "LOOP", "EASING"};
default: return {"ITEMCOLOR1", "ITEMCOLOR2", "CONVEYOR", "TILESET", "UP", "DOWN", "LEFT", "RIGHT"};
}
}
@@ -1856,50 +1887,56 @@ auto MapEditor::setPlatformProperty(const std::string& property, const std::stri
return "animation: " + anim;
}
if (property == "VX") {
if (property == "SPEED") {
try {
platform.vx = std::stof(value);
platform.speed = std::stof(value);
} catch (...) { return "Invalid value: " + value; }
platform.vy = 0.0F;
room_->getPlatformManager()->getPlatform(selection_.index)->resetToInitialPosition(platform);
autosave();
return "vx: " + std::to_string(static_cast<int>(platform.vx)) + " vy: 0";
return "speed: " + std::to_string(static_cast<int>(platform.speed));
}
if (property == "VY") {
try {
platform.vy = std::stof(value);
} catch (...) { return "Invalid value: " + value; }
platform.vx = 0.0F;
if (property == "LOOP") {
std::string val = toLower(value);
if (val == "circular") {
platform.loop = LoopMode::CIRCULAR;
} else {
platform.loop = LoopMode::PINGPONG;
val = "pingpong";
}
room_->getPlatformManager()->getPlatform(selection_.index)->resetToInitialPosition(platform);
autosave();
return "vy: " + std::to_string(static_cast<int>(platform.vy)) + " vx: 0";
return "loop: " + val;
}
return "Unknown property: " + property + " (use: animation, vx, vy)";
if (property == "EASING") {
platform.easing = toLower(value);
room_->getPlatformManager()->getPlatform(selection_.index)->resetToInitialPosition(platform);
autosave();
return "easing: " + platform.easing;
}
return "Unknown property: " + property + " (use: animation, speed, loop, easing)";
}
// Crea una nueva plataforma con valores por defecto, centrada en la habitación
auto MapEditor::addPlatform() -> std::string {
if (!active_) { return "Editor not active"; }
constexpr float CENTER_X = PlayArea::CENTER_X;
constexpr float CENTER_Y = PlayArea::CENTER_Y;
constexpr float ROUTE_HALF = 2.5F * Tile::SIZE; // 2.5 tiles a cada lado (5 tiles total)
MovingPlatform::Data new_platform;
new_platform.animation_path = "bin.yaml";
new_platform.x = CENTER_X;
new_platform.y = CENTER_Y;
new_platform.vx = 24.0F;
new_platform.vy = 0.0F;
new_platform.x1 = static_cast<int>(CENTER_X - ROUTE_HALF);
new_platform.y1 = static_cast<int>(CENTER_Y);
new_platform.x2 = static_cast<int>(CENTER_X + ROUTE_HALF);
new_platform.y2 = static_cast<int>(CENTER_Y);
new_platform.frame = 0;
new_platform.speed = 24.0F;
new_platform.frame = -1;
constexpr float CENTER_X = PlayArea::CENTER_X;
constexpr float CENTER_Y = PlayArea::CENTER_Y;
constexpr float ROUTE_HALF = 2.0F * Tile::SIZE;
new_platform.path = {
{CENTER_X - ROUTE_HALF, CENTER_Y, 0.0F},
{CENTER_X + ROUTE_HALF, CENTER_Y, 0.0F}
};
room_data_.platforms.push_back(new_platform);
room_->getPlatformManager()->addPlatform(std::make_shared<MovingPlatform>(new_platform));
@@ -1937,9 +1974,9 @@ auto MapEditor::duplicatePlatform() -> std::string {
if (!hasSelectedPlatform()) { return "No platform selected"; }
MovingPlatform::Data copy = room_data_.platforms[selection_.index];
copy.x += Tile::SIZE;
copy.x1 += Tile::SIZE;
copy.x2 += Tile::SIZE;
for (auto& wp : copy.path) {
wp.x += Tile::SIZE;
}
room_data_.platforms.push_back(copy);
room_->getPlatformManager()->addPlatform(std::make_shared<MovingPlatform>(copy));

View File

@@ -162,23 +162,18 @@ auto RoomSaver::buildYAML(const fkyaml::node& original_yaml, const Room::Data& r
out << "platforms:\n";
for (const auto& plat : room_data.platforms) {
out << " - animation: " << plat.animation_path << "\n";
int pos_x = static_cast<int>(std::round(plat.x / Tile::SIZE));
int pos_y = static_cast<int>(std::round(plat.y / Tile::SIZE));
out << " position: {x: " << pos_x << ", y: " << pos_y << "}\n";
out << " velocity: {x: " << plat.vx << ", y: " << plat.vy << "}\n";
int b1_x = plat.x1 / Tile::SIZE;
int b1_y = plat.y1 / Tile::SIZE;
int b2_x = plat.x2 / Tile::SIZE;
int b2_y = plat.y2 / Tile::SIZE;
out << " boundaries:\n";
out << " position1: {x: " << b1_x << ", y: " << b1_y << "}\n";
out << " position2: {x: " << b2_x << ", y: " << b2_y << "}\n";
out << " speed: " << plat.speed << "\n";
out << " loop: " << (plat.loop == LoopMode::CIRCULAR ? "circular" : "pingpong") << "\n";
if (plat.easing != "linear") { out << " easing: " << plat.easing << "\n"; }
if (plat.frame != -1) { out << " frame: " << plat.frame << "\n"; }
out << " path:\n";
for (const auto& wp : plat.path) {
int wx = static_cast<int>(std::round(wp.x / Tile::SIZE));
int wy = static_cast<int>(std::round(wp.y / Tile::SIZE));
out << " - {x: " << wx << ", y: " << wy;
if (wp.wait > 0.0F) { out << ", wait: " << wp.wait; }
out << "}\n";
}
out << "\n";
}
}