fixos en la plataforma mobil
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user