afegir i borrar rooms
This commit is contained in:
@@ -5,8 +5,10 @@
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cmath> // Para std::round
|
||||
#include <cstdio> // Para std::remove (borrar fichero)
|
||||
#include <fstream> // Para ifstream, ofstream
|
||||
#include <iostream> // Para cout
|
||||
#include <set> // Para set
|
||||
|
||||
#include "core/input/mouse.hpp" // Para Mouse
|
||||
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||
@@ -14,6 +16,7 @@
|
||||
#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 "core/resources/resource_types.hpp" // Para RoomResource
|
||||
#include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar
|
||||
#include "game/game_control.hpp" // Para GameControl
|
||||
#include "game/editor/room_saver.hpp" // Para RoomSaver
|
||||
@@ -131,6 +134,16 @@ void MapEditor::toggleMiniMap() {
|
||||
}
|
||||
mini_map_visible_ = !mini_map_visible_;
|
||||
if (mini_map_visible_) {
|
||||
// Reconstruir el minimapa (pueden haber cambiado rooms, conexiones, tiles)
|
||||
mini_map_ = std::make_unique<MiniMap>(parseColor(settings_.minimap_bg), parseColor(settings_.minimap_conn));
|
||||
mini_map_->on_navigate = [this](const std::string& room_name) {
|
||||
mini_map_visible_ = false;
|
||||
reenter_ = true;
|
||||
if (GameControl::exit_editor) { GameControl::exit_editor(); }
|
||||
if (GameControl::change_room && GameControl::change_room(room_name)) {
|
||||
if (GameControl::enter_editor) { GameControl::enter_editor(); }
|
||||
}
|
||||
};
|
||||
mini_map_->centerOnRoom(room_path_);
|
||||
}
|
||||
}
|
||||
@@ -1147,27 +1160,59 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
|
||||
if (property == "UP" || property == "DOWN" || property == "LEFT" || property == "RIGHT") {
|
||||
std::string connection = "0";
|
||||
if (val != "0" && val != "null" && val != "none") {
|
||||
// Convertir número a fichero: "6" → "06.yaml"
|
||||
try {
|
||||
int num = std::stoi(val);
|
||||
char buf[16];
|
||||
std::snprintf(buf, sizeof(buf), "%02d.yaml", num);
|
||||
connection = buf;
|
||||
} catch (...) {
|
||||
// Si no es número, asumir que es un nombre de fichero
|
||||
connection = val;
|
||||
if (connection.find('.') == std::string::npos) { connection += ".yaml"; }
|
||||
}
|
||||
}
|
||||
|
||||
if (property == "UP") {
|
||||
room_data_.upper_room = connection;
|
||||
} else if (property == "DOWN") {
|
||||
room_data_.lower_room = connection;
|
||||
} else if (property == "LEFT") {
|
||||
room_data_.left_room = connection;
|
||||
} else {
|
||||
room_data_.right_room = connection;
|
||||
// Dirección opuesta para la conexión recíproca
|
||||
std::string opposite;
|
||||
std::string* our_field = nullptr;
|
||||
if (property == "UP") { opposite = "lower_room"; our_field = &room_data_.upper_room; }
|
||||
if (property == "DOWN") { opposite = "upper_room"; our_field = &room_data_.lower_room; }
|
||||
if (property == "LEFT") { opposite = "right_room"; our_field = &room_data_.left_room; }
|
||||
if (property == "RIGHT") { opposite = "left_room"; our_field = &room_data_.right_room; }
|
||||
|
||||
// Si había una conexión anterior, romper la recíproca de la otra room
|
||||
if (our_field != nullptr && *our_field != "0" && !our_field->empty()) {
|
||||
auto old_other = Resource::Cache::get()->getRoom(*our_field);
|
||||
if (old_other) {
|
||||
if (opposite == "upper_room") { old_other->upper_room = "0"; }
|
||||
else if (opposite == "lower_room") { old_other->lower_room = "0"; }
|
||||
else if (opposite == "left_room") { old_other->left_room = "0"; }
|
||||
else if (opposite == "right_room") { old_other->right_room = "0"; }
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aplicar la nueva conexión
|
||||
if (our_field != nullptr) { *our_field = connection; }
|
||||
|
||||
// Crear la conexión recíproca en la otra room
|
||||
if (connection != "0") {
|
||||
auto other = Resource::Cache::get()->getRoom(connection);
|
||||
if (other) {
|
||||
if (opposite == "upper_room") { other->upper_room = room_path_; }
|
||||
else if (opposite == "lower_room") { other->lower_room = room_path_; }
|
||||
else if (opposite == "left_room") { other->left_room = room_path_; }
|
||||
else if (opposite == "right_room") { other->right_room = room_path_; }
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
autosave();
|
||||
@@ -1177,6 +1222,267 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
|
||||
return "Unknown property: " + property + " (use: bgcolor, border, itemcolor1, itemcolor2, conveyor, tileset, up, down, left, right)";
|
||||
}
|
||||
|
||||
// Obtiene la ruta de assets.yaml derivada de la ruta de una room
|
||||
auto MapEditor::getAssetsYamlPath() -> std::string {
|
||||
std::string ref_path = Resource::List::get()->get(room_path_);
|
||||
if (ref_path.empty()) { return ""; }
|
||||
// ref_path es algo como .../data/room/03.yaml → queremos .../config/assets.yaml
|
||||
auto pos = ref_path.find("/data/room/");
|
||||
if (pos == std::string::npos) { return ""; }
|
||||
return ref_path.substr(0, pos) + "/config/assets.yaml";
|
||||
}
|
||||
|
||||
// Añade una room a assets.yaml (bajo la sección rooms:)
|
||||
void MapEditor::addRoomToAssetsYaml(const std::string& room_name) {
|
||||
std::string assets_path = getAssetsYamlPath();
|
||||
if (assets_path.empty()) { return; }
|
||||
|
||||
// Leer el fichero
|
||||
std::ifstream in(assets_path);
|
||||
if (!in.is_open()) { return; }
|
||||
std::string content((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
in.close();
|
||||
|
||||
// Buscar la última entrada de room y añadir después
|
||||
std::string entry = " - type: ROOM\n path: ${PREFIX}/data/room/" + room_name + "\n";
|
||||
auto last_room = content.rfind("path: ${PREFIX}/data/room/");
|
||||
if (last_room != std::string::npos) {
|
||||
auto end_of_line = content.find('\n', last_room);
|
||||
if (end_of_line != std::string::npos) {
|
||||
content.insert(end_of_line + 1, entry);
|
||||
}
|
||||
}
|
||||
|
||||
std::ofstream out(assets_path);
|
||||
if (out.is_open()) {
|
||||
out << content;
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Quita una room de assets.yaml
|
||||
void MapEditor::removeRoomFromAssetsYaml(const std::string& room_name) {
|
||||
std::string assets_path = getAssetsYamlPath();
|
||||
if (assets_path.empty()) { return; }
|
||||
|
||||
std::ifstream in(assets_path);
|
||||
if (!in.is_open()) { return; }
|
||||
std::string content((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
in.close();
|
||||
|
||||
// Buscar "path: ${PREFIX}/data/room/XX.yaml" y eliminar esa entry (2 líneas: type + path)
|
||||
std::string search = "path: ${PREFIX}/data/room/" + room_name;
|
||||
auto pos = content.find(search);
|
||||
if (pos != std::string::npos) {
|
||||
// Retroceder hasta " - type: ROOM\n"
|
||||
auto line_start = content.rfind(" - type: ROOM", pos);
|
||||
// Avanzar hasta el fin de la línea del path
|
||||
auto line_end = content.find('\n', pos);
|
||||
if (line_start != std::string::npos && line_end != std::string::npos) {
|
||||
content.erase(line_start, line_end - line_start + 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::ofstream out(assets_path);
|
||||
if (out.is_open()) {
|
||||
out << content;
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Crea una nueva habitación
|
||||
auto MapEditor::createNewRoom(const std::string& direction) -> std::string {
|
||||
if (!active_) { return "Editor not active"; }
|
||||
|
||||
// Validar dirección si se proporcionó
|
||||
if (!direction.empty() && direction != "LEFT" && direction != "RIGHT" && direction != "UP" && direction != "DOWN") {
|
||||
return "usage: room new [left|right|up|down]";
|
||||
}
|
||||
|
||||
// Comprobar que no hay ya una room en esa dirección
|
||||
if (!direction.empty()) {
|
||||
std::string* existing = nullptr;
|
||||
if (direction == "UP") { existing = &room_data_.upper_room; }
|
||||
else if (direction == "DOWN") { existing = &room_data_.lower_room; }
|
||||
else if (direction == "LEFT") { existing = &room_data_.left_room; }
|
||||
else if (direction == "RIGHT") { existing = &room_data_.right_room; }
|
||||
if (existing != nullptr && *existing != "0" && !existing->empty()) {
|
||||
return "Already connected " + toLower(direction) + ": " + *existing;
|
||||
}
|
||||
}
|
||||
|
||||
// Encontrar el primer número libre (reutiliza huecos)
|
||||
auto& rooms = Resource::Cache::get()->getRooms();
|
||||
std::set<int> used;
|
||||
for (const auto& r : rooms) {
|
||||
try { used.insert(std::stoi(r.name.substr(0, r.name.find('.')))); }
|
||||
catch (...) {}
|
||||
}
|
||||
int new_num = 1;
|
||||
while (used.contains(new_num)) { ++new_num; }
|
||||
char name_buf[16];
|
||||
std::snprintf(name_buf, sizeof(name_buf), "%02d.yaml", new_num);
|
||||
std::string new_name = name_buf;
|
||||
|
||||
// Derivar la ruta de la carpeta de rooms
|
||||
std::string ref_path = Resource::List::get()->get(room_path_);
|
||||
if (ref_path.empty()) { return "Error: cannot resolve room path"; }
|
||||
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;
|
||||
new_room.number = std::string(name_buf).substr(0, std::string(name_buf).find('.'));
|
||||
new_room.name = "NO_NAME";
|
||||
new_room.bg_color = "black";
|
||||
new_room.border_color = "magenta";
|
||||
new_room.tile_set_file = "standard.gif";
|
||||
new_room.item_color1 = "bright_cyan";
|
||||
new_room.item_color2 = "yellow";
|
||||
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(32 * 16, -1);
|
||||
|
||||
// Conexión recíproca: la nueva room conecta de vuelta a la actual
|
||||
if (direction == "UP") { new_room.lower_room = room_path_; }
|
||||
else if (direction == "DOWN") { new_room.upper_room = room_path_; }
|
||||
else if (direction == "LEFT") { new_room.right_room = room_path_; }
|
||||
else if (direction == "RIGHT") { new_room.left_room = room_path_; }
|
||||
|
||||
// Conexiones del YAML
|
||||
auto connStr = [](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 << " bgColor: black\n";
|
||||
file << " border: magenta\n";
|
||||
file << " tileSetFile: standard.gif\n";
|
||||
file << "\n";
|
||||
file << " connections:\n";
|
||||
file << " up: " << connStr(new_room.upper_room) << "\n";
|
||||
file << " down: " << connStr(new_room.lower_room) << "\n";
|
||||
file << " left: " << connStr(new_room.left_room) << "\n";
|
||||
file << " right: " << connStr(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 < 16; ++row) {
|
||||
file << " - [";
|
||||
for (int col = 0; col < 32; ++col) {
|
||||
file << "-1";
|
||||
if (col < 31) { file << ", "; }
|
||||
}
|
||||
file << "]\n";
|
||||
}
|
||||
file.close();
|
||||
|
||||
// Registrar en Resource::List, cache y assets.yaml
|
||||
Resource::List::get()->add(new_path, Resource::List::Type::ROOM, true, true);
|
||||
Resource::Cache::get()->getRooms().emplace_back(
|
||||
RoomResource{.name = new_name, .room = std::make_shared<Room::Data>(new_room)});
|
||||
addRoomToAssetsYaml(new_name);
|
||||
|
||||
// Conectar la room actual con la nueva (recíproco: ya hecho arriba para la nueva)
|
||||
if (direction == "UP") { room_data_.upper_room = new_name; }
|
||||
else if (direction == "DOWN") { room_data_.lower_room = new_name; }
|
||||
else if (direction == "LEFT") { room_data_.left_room = new_name; }
|
||||
else if (direction == "RIGHT") { room_data_.right_room = new_name; }
|
||||
|
||||
if (!direction.empty()) { autosave(); }
|
||||
|
||||
// Navegar a la nueva room
|
||||
reenter_ = true;
|
||||
if (GameControl::exit_editor) { GameControl::exit_editor(); }
|
||||
if (GameControl::change_room && GameControl::change_room(new_name)) {
|
||||
if (GameControl::enter_editor) { GameControl::enter_editor(); }
|
||||
}
|
||||
|
||||
return "Created room " + new_name + (direction.empty() ? "" : " (" + toLower(direction) + ")");
|
||||
}
|
||||
|
||||
// Elimina la habitación actual
|
||||
auto MapEditor::deleteRoom() -> std::string {
|
||||
if (!active_) { return "Editor not active"; }
|
||||
|
||||
std::string deleted_name = room_path_;
|
||||
|
||||
// Buscar una room vecina a la que navegar después de borrar
|
||||
std::string target = "0";
|
||||
if (room_data_.upper_room != "0" && !room_data_.upper_room.empty()) { target = room_data_.upper_room; }
|
||||
else if (room_data_.lower_room != "0" && !room_data_.lower_room.empty()) { target = room_data_.lower_room; }
|
||||
else if (room_data_.left_room != "0" && !room_data_.left_room.empty()) { target = room_data_.left_room; }
|
||||
else if (room_data_.right_room != "0" && !room_data_.right_room.empty()) { target = room_data_.right_room; }
|
||||
|
||||
if (target == "0") {
|
||||
// Buscar la primera room que no sea esta
|
||||
for (const auto& r : Resource::Cache::get()->getRooms()) {
|
||||
if (r.name != deleted_name) { target = r.name; break; }
|
||||
}
|
||||
}
|
||||
if (target == "0") { return "Cannot delete: no other room to navigate to"; }
|
||||
|
||||
// Desenlazar todas las conexiones recíprocas
|
||||
auto unlinkReciprocal = [&](const std::string& neighbor, const std::string& field_name) {
|
||||
if (neighbor == "0" || neighbor.empty()) { return; }
|
||||
auto other = Resource::Cache::get()->getRoom(neighbor);
|
||||
if (!other) { return; }
|
||||
if (field_name == "upper_room") { other->upper_room = "0"; }
|
||||
else if (field_name == "lower_room") { other->lower_room = "0"; }
|
||||
else if (field_name == "left_room") { other->left_room = "0"; }
|
||||
else if (field_name == "right_room") { other->right_room = "0"; }
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
||||
unlinkReciprocal(room_data_.upper_room, "lower_room"); // Si nosotros somos su lower
|
||||
unlinkReciprocal(room_data_.lower_room, "upper_room"); // Si nosotros somos su upper
|
||||
unlinkReciprocal(room_data_.left_room, "right_room"); // Si nosotros somos su right
|
||||
unlinkReciprocal(room_data_.right_room, "left_room"); // Si nosotros somos su left
|
||||
|
||||
// Navegar a la room destino antes de borrar
|
||||
reenter_ = true;
|
||||
if (GameControl::exit_editor) { GameControl::exit_editor(); }
|
||||
if (GameControl::change_room) { GameControl::change_room(target); }
|
||||
|
||||
// Borrar el YAML del disco
|
||||
std::string yaml_path = Resource::List::get()->get(deleted_name);
|
||||
if (!yaml_path.empty()) {
|
||||
std::remove(yaml_path.c_str());
|
||||
}
|
||||
|
||||
// Quitar del cache
|
||||
auto& cache_rooms = Resource::Cache::get()->getRooms();
|
||||
cache_rooms.erase(
|
||||
std::remove_if(cache_rooms.begin(), cache_rooms.end(),
|
||||
[&](const RoomResource& r) { return r.name == deleted_name; }),
|
||||
cache_rooms.end());
|
||||
|
||||
// Quitar de assets.yaml
|
||||
removeRoomFromAssetsYaml(deleted_name);
|
||||
|
||||
// Re-entrar al editor en la room destino
|
||||
if (GameControl::enter_editor) { GameControl::enter_editor(); }
|
||||
|
||||
return "Deleted room " + deleted_name + ", moved to " + target;
|
||||
}
|
||||
|
||||
// ¿Hay un item seleccionado?
|
||||
auto MapEditor::hasSelectedItem() const -> bool {
|
||||
return selected_item_ >= 0 && selected_item_ < static_cast<int>(room_data_.items.size());
|
||||
|
||||
Reference in New Issue
Block a user