afegit RoomFormat per a centralitzar la creació i edició de fitxers d'habitacions
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#include <iostream> // Para cout
|
||||
#include <set> // Para set
|
||||
|
||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node (loadSettings)
|
||||
#include "core/input/mouse.hpp" // Para Mouse
|
||||
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
@@ -19,7 +20,7 @@
|
||||
#include "core/resources/resource_list.hpp" // Para Resource::List
|
||||
#include "core/resources/resource_types.hpp" // Para RoomResource
|
||||
#include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar
|
||||
#include "game/editor/room_saver.hpp" // Para RoomSaver
|
||||
#include "game/gameplay/room_format.hpp" // Para RoomFormat
|
||||
#include "game/entities/player.hpp" // Para Player
|
||||
#include "game/game_control.hpp" // Para GameControl
|
||||
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
|
||||
@@ -191,12 +192,8 @@ void MapEditor::enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player
|
||||
room_data_ = *room_data_ptr;
|
||||
}
|
||||
|
||||
// Obtener la ruta completa y cargar el YAML original (para edición parcial y backup)
|
||||
// Obtener la ruta completa al fichero del editor (para autosave en disco)
|
||||
file_path_ = Resource::List::get()->get(room_path_);
|
||||
if (!file_path_.empty()) {
|
||||
yaml_ = RoomSaver::loadYAML(file_path_);
|
||||
yaml_backup_ = yaml_; // Copia profunda para revert
|
||||
}
|
||||
|
||||
bool is_reenter = reenter_;
|
||||
if (!reenter_) {
|
||||
@@ -278,13 +275,12 @@ auto MapEditor::revert() -> std::string {
|
||||
if (!active_) { return "Editor not active"; }
|
||||
if (file_path_.empty()) { return "Error: No file path"; }
|
||||
|
||||
// Restaurar el YAML original y reescribir el fichero
|
||||
yaml_ = yaml_backup_;
|
||||
// Restaurar room_data_ desde el cache (que mantiene la versión original) y persistir
|
||||
auto room_data_ptr = Resource::Cache::get()->getRoom(room_path_);
|
||||
if (room_data_ptr) {
|
||||
room_data_ = *room_data_ptr;
|
||||
}
|
||||
RoomSaver::saveYAML(file_path_, yaml_, room_data_);
|
||||
RoomFormat::saveYAML(file_path_, room_data_);
|
||||
|
||||
// Rebuild all entities from room_data_
|
||||
auto* enemy_mgr = room_->getEnemyManager();
|
||||
@@ -334,7 +330,7 @@ void MapEditor::autosave() {
|
||||
|
||||
// Platforms are already synced via resetToInitialPosition during drag commit
|
||||
|
||||
RoomSaver::saveYAML(file_path_, yaml_, room_data_);
|
||||
RoomFormat::saveYAML(file_path_, room_data_);
|
||||
}
|
||||
|
||||
// Actualiza el editor
|
||||
@@ -1573,8 +1569,7 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
|
||||
// Guardar la otra room
|
||||
std::string other_path = Resource::List::get()->get(*our_field);
|
||||
if (!other_path.empty()) {
|
||||
auto other_yaml = RoomSaver::loadYAML(other_path);
|
||||
RoomSaver::saveYAML(other_path, other_yaml, *old_other);
|
||||
RoomFormat::saveYAML(other_path, *old_other);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1610,8 +1605,7 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
|
||||
}
|
||||
std::string other_path = Resource::List::get()->get(connection);
|
||||
if (!other_path.empty()) {
|
||||
auto other_yaml = RoomSaver::loadYAML(other_path);
|
||||
RoomSaver::saveYAML(other_path, other_yaml, *other);
|
||||
RoomFormat::saveYAML(other_path, *other);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1669,19 +1663,9 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string { //
|
||||
std::string room_dir = ref_path.substr(0, ref_path.find_last_of("\\/") + 1);
|
||||
std::string new_path = room_dir + new_name;
|
||||
|
||||
// Crear Room::Data por defecto con conexión recíproca
|
||||
Room::Data new_room;
|
||||
// Construir Room::Data por defecto desde la autoridad del formato
|
||||
Room::Data new_room = RoomFormat::createDefault();
|
||||
new_room.number = std::string(name_buf).substr(0, std::string(name_buf).find('.'));
|
||||
new_room.tile_set_file = "standard.gif";
|
||||
new_room.item_color1 = 11;
|
||||
new_room.item_color2 = 12;
|
||||
new_room.upper_room = "0";
|
||||
new_room.lower_room = "0";
|
||||
new_room.left_room = "0";
|
||||
new_room.right_room = "0";
|
||||
new_room.conveyor_belt_direction = 0;
|
||||
new_room.tile_map.resize(Map::WIDTH * Map::HEIGHT, -1);
|
||||
new_room.collision_tile_map.resize(Map::WIDTH * Map::HEIGHT, 0);
|
||||
|
||||
// Conexión recíproca: la nueva room conecta de vuelta a la actual
|
||||
if (direction == "UP") {
|
||||
@@ -1694,40 +1678,9 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string { //
|
||||
new_room.left_room = room_path_;
|
||||
}
|
||||
|
||||
// Conexiones del YAML
|
||||
auto conn_str = [](const std::string& c) -> std::string { return (c == "0") ? "null" : c; };
|
||||
|
||||
// Crear el YAML
|
||||
std::ofstream file(new_path);
|
||||
if (!file.is_open()) { return "Error: cannot create " + new_path; }
|
||||
|
||||
file << "# NO_NAME\n";
|
||||
file << "room:\n";
|
||||
file << " name_en: \"NO_NAME\"\n";
|
||||
file << " name_ca: \"NO_NAME\"\n";
|
||||
file << " tileSetFile: standard.gif\n";
|
||||
file << "\n";
|
||||
file << " connections:\n";
|
||||
file << " up: " << conn_str(new_room.upper_room) << "\n";
|
||||
file << " down: " << conn_str(new_room.lower_room) << "\n";
|
||||
file << " left: " << conn_str(new_room.left_room) << "\n";
|
||||
file << " right: " << conn_str(new_room.right_room) << "\n";
|
||||
file << "\n";
|
||||
file << " itemColor1: bright_cyan\n";
|
||||
file << " itemColor2: yellow\n";
|
||||
file << "\n";
|
||||
file << " conveyorBelt: none\n";
|
||||
file << "\n";
|
||||
file << "tilemap:\n";
|
||||
for (int row = 0; row < Map::HEIGHT; ++row) {
|
||||
file << " - [";
|
||||
for (int col = 0; col < Map::WIDTH; ++col) {
|
||||
file << "-1";
|
||||
if (col < Map::WIDTH - 1) { file << ", "; }
|
||||
}
|
||||
file << "]\n";
|
||||
}
|
||||
file.close();
|
||||
// Persistir vía la autoridad del formato (no más std::ofstream a pelo)
|
||||
auto save_result = RoomFormat::saveYAML(new_path, new_room);
|
||||
if (save_result.find("Error") == 0) { return save_result; }
|
||||
|
||||
// Registrar en Resource::List (mapa + assets.yaml) y cache
|
||||
Resource::List::get()->addAsset(new_path, Resource::List::Type::ROOM);
|
||||
@@ -1803,8 +1756,7 @@ auto MapEditor::deleteRoom() -> std::string { // NOLINT(readability-function-co
|
||||
// Guardar la otra room
|
||||
std::string other_path = Resource::List::get()->get(neighbor);
|
||||
if (!other_path.empty()) {
|
||||
auto other_yaml = RoomSaver::loadYAML(other_path);
|
||||
RoomSaver::saveYAML(other_path, other_yaml, *other);
|
||||
RoomFormat::saveYAML(other_path, *other);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <memory> // Para shared_ptr, unique_ptr
|
||||
#include <string> // Para string
|
||||
|
||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||
#include "game/editor/mini_map.hpp" // Para MiniMap
|
||||
#include "game/editor/tile_picker.hpp" // Para TilePicker
|
||||
#include "game/entities/enemy.hpp" // Para Enemy::Data
|
||||
@@ -172,10 +171,6 @@ class MapEditor {
|
||||
std::string room_path_;
|
||||
std::string file_path_;
|
||||
|
||||
// YAML: nodo original (para campos que no se editan: name_ca, etc.)
|
||||
fkyaml::node yaml_;
|
||||
fkyaml::node yaml_backup_;
|
||||
|
||||
// Referencias a objetos vivos
|
||||
std::shared_ptr<Room> room_;
|
||||
std::shared_ptr<Player> player_;
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
#ifdef _DEBUG
|
||||
|
||||
#include "game/editor/room_saver.hpp"
|
||||
|
||||
#include <cmath> // Para std::round
|
||||
#include <fstream> // Para ifstream, ofstream, istreambuf_iterator
|
||||
#include <iostream> // Para cout, cerr
|
||||
#include <sstream> // Para ostringstream
|
||||
|
||||
#include "utils/defines.hpp" // Para Tile::SIZE
|
||||
|
||||
// Carga el YAML original directamente del filesystem (no del resource pack)
|
||||
auto RoomSaver::loadYAML(const std::string& file_path) -> fkyaml::node {
|
||||
std::ifstream file(file_path);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "RoomSaver: Cannot open " << file_path << "\n";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
return fkyaml::node::deserialize(content);
|
||||
}
|
||||
|
||||
// Convierte una room connection al formato YAML
|
||||
auto RoomSaver::roomConnectionToYAML(const std::string& connection) -> std::string {
|
||||
if (connection == "0" || connection.empty()) { return "null"; }
|
||||
return connection;
|
||||
}
|
||||
|
||||
// Convierte la dirección del conveyor belt a string
|
||||
auto RoomSaver::conveyorBeltToString(int direction) -> std::string {
|
||||
if (direction < 0) { return "left"; }
|
||||
if (direction > 0) { return "right"; }
|
||||
return "none";
|
||||
}
|
||||
|
||||
// Genera el YAML completo como texto con formato compacto
|
||||
auto RoomSaver::buildYAML(const fkyaml::node& original_yaml, const Room::Data& room_data) -> std::string { // NOLINT(readability-function-cognitive-complexity)
|
||||
(void)original_yaml; // Ya no se usa; mantenido para compatibilidad con la firma de saveYAML
|
||||
std::ostringstream out;
|
||||
|
||||
// --- Sección room ---
|
||||
out << "room:\n";
|
||||
|
||||
// zone es siempre obligatoria
|
||||
out << " zone: " << room_data.zone << "\n";
|
||||
|
||||
// bgColor ya no se escribe en el YAML
|
||||
// tileSetFile solo si es override explícito del valor heredado de la zona
|
||||
if (room_data.tile_set_overridden) {
|
||||
out << " tileSetFile: " << room_data.tile_set_file << "\n";
|
||||
}
|
||||
|
||||
// music solo si es override explícito del valor heredado de la zona
|
||||
if (room_data.music_overridden) {
|
||||
out << " music: " << room_data.music << "\n";
|
||||
}
|
||||
|
||||
// Conexiones
|
||||
out << "\n";
|
||||
out << " # Conexiones de la habitación (null = sin conexión)\n";
|
||||
out << " connections:\n";
|
||||
out << " up: " << roomConnectionToYAML(room_data.upper_room) << "\n";
|
||||
out << " down: " << roomConnectionToYAML(room_data.lower_room) << "\n";
|
||||
out << " left: " << roomConnectionToYAML(room_data.left_room) << "\n";
|
||||
out << " right: " << roomConnectionToYAML(room_data.right_room) << "\n";
|
||||
|
||||
// Colores de items
|
||||
out << "\n";
|
||||
out << " # Colores de los objetos\n";
|
||||
out << " itemColor1: " << static_cast<int>(room_data.item_color1) << "\n";
|
||||
out << " itemColor2: " << static_cast<int>(room_data.item_color2) << "\n";
|
||||
|
||||
// Conveyor belt
|
||||
out << "\n";
|
||||
out << " # Dirección de la cinta transportadora: left, none, right\n";
|
||||
out << " conveyorBelt: " << conveyorBeltToString(room_data.conveyor_belt_direction) << "\n";
|
||||
|
||||
// --- Tilemap (MAP_HEIGHT filas × MAP_WIDTH columnas, formato flow) ---
|
||||
out << "\n";
|
||||
out << "# Tilemap: " << Map::HEIGHT << " filas x " << Map::WIDTH << " columnas @ " << Tile::SIZE << "px/tile\n";
|
||||
out << "tilemap:\n";
|
||||
|
||||
// Mapa de dibujo
|
||||
out << " # Mapa de dibujo (indices de tiles, -1 = vacio)\n";
|
||||
out << " draw:\n";
|
||||
for (int row = 0; row < Map::HEIGHT; ++row) {
|
||||
out << " - [";
|
||||
for (int col = 0; col < Map::WIDTH; ++col) {
|
||||
int index = (row * Map::WIDTH) + col;
|
||||
if (index < static_cast<int>(room_data.tile_map.size())) {
|
||||
out << room_data.tile_map[index];
|
||||
} else {
|
||||
out << -1;
|
||||
}
|
||||
if (col < Map::WIDTH - 1) { out << ", "; }
|
||||
}
|
||||
out << "]\n";
|
||||
}
|
||||
|
||||
// Mapa de colisiones
|
||||
out << " # Mapa de colisiones (0 = vacio, 1 = solido)\n";
|
||||
out << " collision:\n";
|
||||
for (int row = 0; row < Map::HEIGHT; ++row) {
|
||||
out << " - [";
|
||||
for (int col = 0; col < Map::WIDTH; ++col) {
|
||||
int index = (row * Map::WIDTH) + col;
|
||||
if (index < static_cast<int>(room_data.collision_tile_map.size())) {
|
||||
out << room_data.collision_tile_map[index];
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
if (col < Map::WIDTH - 1) { out << ", "; }
|
||||
}
|
||||
out << "]\n";
|
||||
}
|
||||
|
||||
// --- Enemigos ---
|
||||
if (!room_data.enemies.empty()) {
|
||||
out << "\n";
|
||||
out << "# Enemigos en esta habitación\n";
|
||||
out << "enemies:\n";
|
||||
for (const auto& enemy : room_data.enemies) {
|
||||
out << " - animation: " << enemy.animation_path << "\n";
|
||||
if (enemy.type != "path") { out << " type: " << enemy.type << "\n"; }
|
||||
|
||||
int pos_x = static_cast<int>(std::round(enemy.x / Tile::SIZE));
|
||||
int pos_y = static_cast<int>(std::round(enemy.y / Tile::SIZE));
|
||||
out << " position: {x: " << pos_x << ", y: " << pos_y << "}\n";
|
||||
|
||||
out << " velocity: {x: " << enemy.vx << ", y: " << enemy.vy << "}\n";
|
||||
|
||||
int b1_x = enemy.x1 / Tile::SIZE;
|
||||
int b1_y = enemy.y1 / Tile::SIZE;
|
||||
int b2_x = enemy.x2 / Tile::SIZE;
|
||||
int b2_y = enemy.y2 / Tile::SIZE;
|
||||
out << " boundaries:\n";
|
||||
out << " position1: {x: " << b1_x << ", y: " << b1_y << "}\n";
|
||||
out << " position2: {x: " << b2_x << ", y: " << b2_y << "}\n";
|
||||
|
||||
if (enemy.flip) { out << " flip: true\n"; }
|
||||
if (enemy.mirror) { out << " mirror: true\n"; }
|
||||
if (enemy.frame != -1) { out << " frame: " << enemy.frame << "\n"; }
|
||||
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// --- Items ---
|
||||
if (!room_data.items.empty()) {
|
||||
out << "# Objetos en esta habitación\n";
|
||||
out << "items:\n";
|
||||
for (const auto& item : room_data.items) {
|
||||
out << " - tileSetFile: " << item.tile_set_file << "\n";
|
||||
out << " tile: " << item.tile << "\n";
|
||||
|
||||
int item_x = static_cast<int>(std::round(item.x / Tile::SIZE));
|
||||
int item_y = static_cast<int>(std::round(item.y / Tile::SIZE));
|
||||
out << " position: {x: " << item_x << ", y: " << item_y << "}\n";
|
||||
|
||||
if (item.counter != 0) {
|
||||
out << " counter: " << item.counter << "\n";
|
||||
}
|
||||
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// --- Plataformas ---
|
||||
if (!room_data.platforms.empty()) {
|
||||
out << "# Plataformas móviles en esta habitación\n";
|
||||
out << "platforms:\n";
|
||||
for (const auto& plat : room_data.platforms) {
|
||||
out << " - animation: " << plat.animation_path << "\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";
|
||||
}
|
||||
}
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
// Guarda el YAML a disco
|
||||
auto RoomSaver::saveYAML(const std::string& file_path, const fkyaml::node& original_yaml, const Room::Data& room_data) -> std::string {
|
||||
std::string content = buildYAML(original_yaml, room_data);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif // _DEBUG
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
#include <string> // Para string
|
||||
|
||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||
#include "game/gameplay/room.hpp" // Para Room::Data
|
||||
|
||||
/**
|
||||
* @brief Guardado de archivos YAML de habitaciones para el editor de mapas
|
||||
*
|
||||
* Lee el YAML original con fkyaml (para acceder a todos los campos: name_ca, name_en, etc.)
|
||||
* Genera el YAML como texto formateado compacto (idéntico al formato original de los ficheros).
|
||||
* Solo se usa en builds de debug.
|
||||
*/
|
||||
class RoomSaver {
|
||||
public:
|
||||
RoomSaver() = delete;
|
||||
|
||||
// Carga el YAML original desde disco como nodo fkyaml (lee del filesystem, no del pack)
|
||||
static auto loadYAML(const std::string& file_path) -> fkyaml::node;
|
||||
|
||||
// Genera y guarda el YAML completo a disco
|
||||
// original_yaml: nodo fkyaml con los datos originales (para campos que no se editan: name_ca, etc.)
|
||||
// room_data: datos editados (posiciones de enemigos, items, etc.)
|
||||
static auto saveYAML(const std::string& file_path, const fkyaml::node& original_yaml, const Room::Data& room_data) -> std::string;
|
||||
|
||||
private:
|
||||
static auto buildYAML(const fkyaml::node& original_yaml, const Room::Data& room_data) -> std::string;
|
||||
static auto roomConnectionToYAML(const std::string& connection) -> std::string;
|
||||
static auto conveyorBeltToString(int direction) -> std::string;
|
||||
};
|
||||
|
||||
#endif // _DEBUG
|
||||
Reference in New Issue
Block a user