guardat dels canvis en la habitacio

This commit is contained in:
2026-04-02 11:10:03 +02:00
parent b31346830f
commit a6fae7b001
10 changed files with 796 additions and 65 deletions

View File

@@ -11,7 +11,9 @@
#include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/surface.hpp" // Para Surface
#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/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
@@ -51,12 +53,19 @@ void MapEditor::enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player
room_path_ = room_path;
scoreboard_data_ = std::move(scoreboard_data);
// Cargar una copia de los datos de la habitación (para boundaries y edición futura)
// Cargar una copia de los datos de la habitación (para boundaries y edición)
auto room_data_ptr = Resource::Cache::get()->getRoom(room_path);
if (room_data_ptr) {
room_data_ = *room_data_ptr;
}
// Obtener la ruta completa y cargar el YAML original (para edición parcial y backup)
file_path_ = Resource::List::get()->get(room_path_);
if (!file_path_.empty()) {
yaml_ = RoomSaver::loadYAML(file_path_);
yaml_backup_ = yaml_; // Copia profunda para revert
}
// Guardar estado de invencibilidad y forzarla
invincible_before_editor_ = Options::cheats.invincible;
Options::cheats.invincible = Options::Cheat::State::ENABLED;
@@ -95,6 +104,39 @@ void MapEditor::exit() {
std::cout << "MapEditor: OFF\n";
}
// Revierte todos los cambios al estado original
auto MapEditor::revert() -> std::string {
if (!active_) { return "Editor not active"; }
if (file_path_.empty()) { return "Error: No file path"; }
// Restaurar el YAML al backup original
yaml_ = yaml_backup_;
RoomSaver::saveYAML(file_path_, yaml_);
// Recargar room_data_ desde el backup
auto room_data_ptr = Resource::Cache::get()->getRoom(room_path_);
if (room_data_ptr) {
room_data_ = *room_data_ptr;
}
// Resetear los sprites vivos a las posiciones originales
room_->resetEnemyPositions(room_data_.enemies);
// Resetear items (recargar posiciones)
auto* item_mgr = room_->getItemManager();
for (int i = 0; i < item_mgr->getCount() && i < static_cast<int>(room_data_.items.size()); ++i) {
item_mgr->getItem(i)->setPosition(room_data_.items[i].x, room_data_.items[i].y);
}
return "Reverted to original";
}
// Auto-guarda los cambios puntuales al YAML tras soltar una entidad
void MapEditor::autosave() {
if (file_path_.empty()) { return; }
RoomSaver::saveYAML(file_path_, yaml_);
}
// Actualiza el editor
void MapEditor::update(float delta_time) {
// Mantener el ratón siempre visible
@@ -242,18 +284,22 @@ void MapEditor::handleMouseUp() {
const int SNAP_X = static_cast<int>(drag_.snap_x);
const int SNAP_Y = static_cast<int>(drag_.snap_y);
bool changed = false;
switch (drag_.target) {
case DragTarget::PLAYER:
player_->setDebugPosition(drag_.snap_x, drag_.snap_y);
player_->finalizeDebugTeleport();
// El jugador no se guarda en el YAML de la habitación (es dato de spawn global)
break;
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]);
RoomSaver::updateEnemyPosition(yaml_, IDX, drag_.snap_x, drag_.snap_y);
changed = true;
}
break;
@@ -261,6 +307,8 @@ void MapEditor::handleMouseUp() {
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;
RoomSaver::updateEnemyBound1(yaml_, IDX, SNAP_X, SNAP_Y);
changed = true;
}
break;
@@ -268,12 +316,16 @@ void MapEditor::handleMouseUp() {
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;
RoomSaver::updateEnemyBound2(yaml_, IDX, SNAP_X, SNAP_Y);
changed = true;
}
break;
case DragTarget::ITEM:
if (IDX >= 0 && IDX < room_->getItemManager()->getCount()) {
room_->getItemManager()->getItem(IDX)->setPosition(drag_.snap_x, drag_.snap_y);
RoomSaver::updateItemPosition(yaml_, IDX, drag_.snap_x, drag_.snap_y);
changed = true;
}
break;
@@ -281,6 +333,11 @@ void MapEditor::handleMouseUp() {
break;
}
// Auto-guardar si hubo cambio
if (changed) {
autosave();
}
// Resetear estado de drag
drag_ = {};
}

View File

@@ -7,6 +7,7 @@
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include "external/fkyaml_node.hpp" // Para fkyaml::node
#include "game/entities/enemy.hpp" // Para Enemy::Data
#include "game/entities/item.hpp" // Para Item::Data
#include "game/entities/player.hpp" // Para Player::SpawnData
@@ -29,6 +30,7 @@ class MapEditor {
void update(float delta_time);
void render();
void handleEvent(const SDL_Event& event);
auto revert() -> std::string; // Revierte todos los cambios al estado original
private:
static MapEditor* instance_; // [SINGLETON] Objeto privado
@@ -59,8 +61,9 @@ class MapEditor {
void renderBoundaryMarker(float x, float y, Uint8 color); // Dibuja un marcador de boundary en una posición
void renderSelectionHighlight(); // Dibuja highlight del elemento seleccionado/arrastrado
void handleMouseDown(float game_x, float game_y); // Procesa click del ratón (hit test + inicio de drag)
void handleMouseUp(); // Procesa soltar el ratón (commit del drag)
void handleMouseUp(); // Procesa soltar el ratón (commit del drag + autosave)
void updateDrag(); // Actualiza la posición snapped durante el drag
void autosave(); // Guarda los cambios puntuales al YAML
static auto snapToGrid(float value) -> float; // Alinea un valor a la cuadrícula de 8x8
static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool; // Hit test punto en rectángulo
@@ -68,10 +71,14 @@ class MapEditor {
bool active_{false};
DragState drag_;
// Copia mutable de los datos de la habitación (para edición futura)
Room::Data room_data_;
Player::SpawnData player_spawn_;
std::string room_path_;
// Datos de la habitación
Room::Data room_data_; // Copia mutable (para boundaries y edición)
std::string room_path_; // Nombre del fichero (ej: "06.yaml")
std::string file_path_; // Ruta completa del fichero en disco
// YAML: nodo vivo (se edita parcialmente) y backup para revert
fkyaml::node yaml_; // Nodo YAML actual (editado parcialmente)
fkyaml::node yaml_backup_; // Backup del YAML original al entrar
// Referencias a objetos vivos (para rendering)
std::shared_ptr<Room> room_;

View File

@@ -0,0 +1,118 @@
#ifdef _DEBUG
#include "game/editor/room_saver.hpp"
#include <cmath> // Para std::round
#include <fstream> // Para ofstream
#include <iostream> // Para cout, cerr
#include "core/resources/resource_helper.hpp" // Para Resource::Helper
#include "utils/defines.hpp" // Para Tile::SIZE
// Carga el YAML original desde disco
auto RoomSaver::loadYAML(const std::string& file_path) -> fkyaml::node {
auto file_data = Resource::Helper::loadFile(file_path);
if (file_data.empty()) {
std::cerr << "RoomSaver: Cannot load " << file_path << "\n";
return {};
}
std::string content(file_data.begin(), file_data.end());
return fkyaml::node::deserialize(content);
}
// Guarda el nodo YAML completo a disco
auto RoomSaver::saveYAML(const std::string& file_path, const fkyaml::node& yaml) -> std::string {
std::string content = fkyaml::node::serialize(yaml);
std::ofstream file(file_path);
if (!file.is_open()) {
std::cerr << "RoomSaver: Cannot write to " << file_path << "\n";
return "Error: Cannot write to " + file_path;
}
file << content;
file.close();
const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1);
std::cout << "RoomSaver: Saved " << FILE_NAME << "\n";
return "Saved " + FILE_NAME;
}
// Actualiza la posición inicial de un enemigo (pixels → tiles)
void RoomSaver::updateEnemyPosition(fkyaml::node& yaml, int index, float x, float y) {
if (!yaml.contains("enemies")) { return; }
auto& enemies = yaml["enemies"];
if (index < 0 || index >= static_cast<int>(enemies.size())) { return; }
auto& enemy = enemies[index];
int tile_x = static_cast<int>(std::round(x / Tile::SIZE));
int tile_y = static_cast<int>(std::round(y / Tile::SIZE));
if (!enemy.contains("position")) {
enemy["position"] = fkyaml::node::mapping();
}
enemy["position"]["x"] = tile_x;
enemy["position"]["y"] = tile_y;
}
// Actualiza boundary1 de un enemigo (pixels → tiles)
void RoomSaver::updateEnemyBound1(fkyaml::node& yaml, int index, int x, int y) {
if (!yaml.contains("enemies")) { return; }
auto& enemies = yaml["enemies"];
if (index < 0 || index >= static_cast<int>(enemies.size())) { return; }
auto& enemy = enemies[index];
int tile_x = x / Tile::SIZE;
int tile_y = y / Tile::SIZE;
if (!enemy.contains("boundaries")) {
enemy["boundaries"] = fkyaml::node::mapping();
}
auto& bounds = enemy["boundaries"];
if (!bounds.contains("position1")) {
bounds["position1"] = fkyaml::node::mapping();
}
bounds["position1"]["x"] = tile_x;
bounds["position1"]["y"] = tile_y;
}
// Actualiza boundary2 de un enemigo (pixels → tiles)
void RoomSaver::updateEnemyBound2(fkyaml::node& yaml, int index, int x, int y) {
if (!yaml.contains("enemies")) { return; }
auto& enemies = yaml["enemies"];
if (index < 0 || index >= static_cast<int>(enemies.size())) { return; }
auto& enemy = enemies[index];
int tile_x = x / Tile::SIZE;
int tile_y = y / Tile::SIZE;
if (!enemy.contains("boundaries")) {
enemy["boundaries"] = fkyaml::node::mapping();
}
auto& bounds = enemy["boundaries"];
if (!bounds.contains("position2")) {
bounds["position2"] = fkyaml::node::mapping();
}
bounds["position2"]["x"] = tile_x;
bounds["position2"]["y"] = tile_y;
}
// Actualiza la posición de un item (pixels → tiles)
void RoomSaver::updateItemPosition(fkyaml::node& yaml, int index, float x, float y) {
if (!yaml.contains("items")) { return; }
auto& items = yaml["items"];
if (index < 0 || index >= static_cast<int>(items.size())) { return; }
auto& item = items[index];
int tile_x = static_cast<int>(std::round(x / Tile::SIZE));
int tile_y = static_cast<int>(std::round(y / Tile::SIZE));
if (!item.contains("position")) {
item["position"] = fkyaml::node::mapping();
}
item["position"]["x"] = tile_x;
item["position"]["y"] = tile_y;
}
#endif // _DEBUG

View File

@@ -0,0 +1,36 @@
#pragma once
#ifdef _DEBUG
#include <string> // Para string
#include "external/fkyaml_node.hpp" // Para fkyaml::node
#include "game/entities/enemy.hpp" // Para Enemy::Data
#include "game/entities/item.hpp" // Para Item::Data
#include "game/entities/player.hpp" // Para Player::SpawnData
/**
* @brief Edición parcial de archivos YAML de habitaciones
*
* Lee el YAML original, modifica solo los campos editados y serializa.
* Preserva todos los campos que no se editan (name_ca, comentarios del formato, etc.)
* Solo se usa en builds de debug (editor de mapas).
*/
class RoomSaver {
public:
RoomSaver() = delete;
// Carga el YAML original desde disco (llamar al entrar al editor)
static auto loadYAML(const std::string& file_path) -> fkyaml::node;
// Guarda el nodo YAML completo a disco
static auto saveYAML(const std::string& file_path, const fkyaml::node& yaml) -> std::string;
// Modificaciones puntuales sobre el nodo YAML (posiciones en pixels, se convierten a tiles)
static void updateEnemyPosition(fkyaml::node& yaml, int index, float x, float y);
static void updateEnemyBound1(fkyaml::node& yaml, int index, int x, int y);
static void updateEnemyBound2(fkyaml::node& yaml, int index, int x, int y);
static void updateItemPosition(fkyaml::node& yaml, int index, float x, float y);
};
#endif // _DEBUG