- nou format per a assets.yaml

- ResourceList gestiona addAsset i removeAsset
This commit is contained in:
2026-04-03 08:17:41 +02:00
parent 052607873b
commit dc1470ec0e
5 changed files with 490 additions and 693 deletions

View File

@@ -51,8 +51,97 @@ namespace Resource {
addToMap(file_path, type, required, absolute);
}
// Añade un asset al mapa y lo persiste en assets.yaml
void List::addAsset(const std::string& path, Type type) {
// Añadir al mapa en memoria
addToMap(path, type, true, true);
// Persistir en assets.yaml
if (config_file_path_.empty()) { return; }
std::ifstream in(config_file_path_);
if (!in.is_open()) { return; }
std::string content((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
in.close();
// Construir la ruta con variable ${PREFIX} (invertir la sustitución)
std::string var_path = path;
if (!prefix_.empty() && !executable_path_.empty()) {
std::string full_prefix = executable_path_ + prefix_;
auto pos = var_path.find(full_prefix);
if (pos != std::string::npos) {
var_path.replace(pos, full_prefix.length(), "${PREFIX}");
}
}
// Buscar la última entrada con el mismo prefijo de ruta e insertar después
std::string entry = " - " + var_path + "\n";
auto last_pos = content.rfind(var_path.substr(0, var_path.rfind('/')));
if (last_pos != std::string::npos) {
auto end_of_line = content.find('\n', last_pos);
if (end_of_line != std::string::npos) {
content.insert(end_of_line + 1, entry);
}
}
std::ofstream out(config_file_path_);
if (out.is_open()) {
out << content;
out.close();
}
}
// Quita un asset del mapa y lo elimina de assets.yaml
void List::removeAsset(const std::string& filename) {
// Obtener la ruta antes de borrar del mapa
auto it = file_list_.find(filename);
std::string file_path;
if (it != file_list_.end()) {
file_path = it->second.file;
file_list_.erase(it);
}
// Persistir en assets.yaml
if (config_file_path_.empty() || file_path.empty()) { return; }
std::ifstream in(config_file_path_);
if (!in.is_open()) { return; }
std::string content((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
in.close();
// Construir la ruta con variable ${PREFIX}
std::string var_path = file_path;
if (!prefix_.empty() && !executable_path_.empty()) {
std::string full_prefix = executable_path_ + prefix_;
auto pos = var_path.find(full_prefix);
if (pos != std::string::npos) {
var_path.replace(pos, full_prefix.length(), "${PREFIX}");
}
}
// Buscar la línea con el path y eliminarla
auto pos = content.find(var_path);
if (pos != std::string::npos) {
auto line_start = content.rfind('\n', pos);
line_start = (line_start == std::string::npos) ? 0 : line_start;
auto line_end = content.find('\n', pos);
if (line_end != std::string::npos) {
content.erase(line_start, line_end - line_start);
}
}
std::ofstream out(config_file_path_);
if (out.is_open()) {
out << content;
out.close();
}
}
// Carga recursos desde un archivo de configuración con soporte para variables
void List::loadFromFile(const std::string& config_file_path, const std::string& prefix, const std::string& system_folder) { // NOLINT(readability-convert-member-functions-to-static)
config_file_path_ = config_file_path;
prefix_ = prefix;
std::ifstream file(config_file_path);
if (!file.is_open()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -89,56 +178,87 @@ namespace Resource {
const std::string& category = it.key().get_value<std::string>();
const auto& category_assets = it.value();
// Verificar que es un array
if (!category_assets.is_sequence()) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Category '%s' is not a sequence, skipping",
category.c_str());
continue;
}
if (category_assets.is_mapping()) {
// Nuevo formato: categoría → { TIPO: [paths...], TIPO2: [paths...] }
for (auto type_it = category_assets.begin(); type_it != category_assets.end(); ++type_it) {
try {
auto type_str = type_it.key().get_value<std::string>();
Type type = parseAssetType(type_str);
const auto& items = type_it.value();
// Procesar cada asset en la categoría
for (const auto& asset : category_assets) {
try {
// Verificar campos obligatorios
if (!asset.contains("type") || !asset.contains("path")) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Asset in category '%s' missing 'type' or 'path', skipping",
category.c_str());
continue;
if (!items.is_sequence()) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Type '%s' in category '%s' is not a sequence, skipping",
type_str.c_str(), category.c_str());
continue;
}
for (const auto& item : items) {
try {
if (item.is_string()) {
// Formato simple: solo el path
auto path = replaceVariables(item.get_value<std::string>(), prefix, system_folder);
addToMap(path, type, true, false);
} else if (item.is_mapping() && item.contains("path")) {
// Formato expandido: { path, required?, absolute? }
auto path = replaceVariables(item["path"].get_value<std::string>(), prefix, system_folder);
bool required = !item.contains("required") || item["required"].get_value<bool>();
bool absolute = item.contains("absolute") && item["absolute"].get_value<bool>();
addToMap(path, type, required, absolute);
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Invalid item in type '%s', category '%s', skipping",
type_str.c_str(), category.c_str());
}
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error parsing asset in category '%s', type '%s': %s",
category.c_str(), type_str.c_str(), e.what());
}
}
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error parsing type in category '%s': %s",
category.c_str(), e.what());
}
// Extraer campos
auto type_str = asset["type"].get_value<std::string>();
auto path = asset["path"].get_value<std::string>();
// Valores por defecto
bool required = true;
bool absolute = false;
// Campos opcionales
if (asset.contains("required")) {
required = asset["required"].get_value<bool>();
}
if (asset.contains("absolute")) {
absolute = asset["absolute"].get_value<bool>();
}
// Reemplazar variables en la ruta
path = replaceVariables(path, prefix, system_folder);
// Parsear el tipo de asset
Type type = parseAssetType(type_str);
// Añadir al mapa
addToMap(path, type, required, absolute);
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error parsing asset in category '%s': %s",
category.c_str(),
e.what());
}
} else if (category_assets.is_sequence()) {
// Formato antiguo (retrocompatibilidad): categoría → [{type, path}, ...]
for (const auto& asset : category_assets) {
try {
if (!asset.contains("type") || !asset.contains("path")) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Asset in category '%s' missing 'type' or 'path', skipping",
category.c_str());
continue;
}
auto type_str = asset["type"].get_value<std::string>();
auto path = asset["path"].get_value<std::string>();
bool required = true;
bool absolute = false;
if (asset.contains("required")) {
required = asset["required"].get_value<bool>();
}
if (asset.contains("absolute")) {
absolute = asset["absolute"].get_value<bool>();
}
path = replaceVariables(path, prefix, system_folder);
Type type = parseAssetType(type_str);
addToMap(path, type, required, absolute);
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error parsing asset in category '%s': %s",
category.c_str(), e.what());
}
}
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Category '%s' has invalid format, skipping",
category.c_str());
}
}

View File

@@ -33,6 +33,8 @@ namespace Resource {
// --- Métodos para la gestión de recursos ---
void add(const std::string& file_path, Type type, bool required = true, bool absolute = false);
void addAsset(const std::string& path, Type type); // Añade al mapa y persiste en assets.yaml
void removeAsset(const std::string& filename); // Quita del mapa y persiste en assets.yaml
void loadFromFile(const std::string& config_file_path, const std::string& prefix = "", const std::string& system_folder = ""); // Con soporte para variables
void loadFromString(const std::string& config_content, const std::string& prefix = "", const std::string& system_folder = ""); // Para cargar desde pack (release)
[[nodiscard]] auto get(const std::string& filename) const -> std::string; // Obtiene la ruta completa
@@ -56,6 +58,8 @@ namespace Resource {
// --- Variables internas ---
std::unordered_map<std::string, Item> file_list_; // Mapa para búsqueda O(1)
std::string executable_path_; // Ruta del ejecutable
std::string config_file_path_; // Ruta del fichero assets.yaml
std::string prefix_; // Prefijo para rutas (${PREFIX})
// --- Métodos internos ---
[[nodiscard]] static auto getTypeName(Type type) -> std::string; // Obtiene el nombre del tipo

View File

@@ -1266,74 +1266,6 @@ 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"; }
@@ -1444,11 +1376,10 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string {
}
file.close();
// Registrar en Resource::List, cache y assets.yaml
Resource::List::get()->add(new_path, Resource::List::Type::ROOM, true, true);
// Registrar en Resource::List (mapa + assets.yaml) y cache
Resource::List::get()->addAsset(new_path, Resource::List::Type::ROOM);
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") {
@@ -1546,8 +1477,8 @@ auto MapEditor::deleteRoom() -> std::string {
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);
// Quitar de Resource::List (mapa + assets.yaml)
Resource::List::get()->removeAsset(deleted_name);
// Re-entrar al editor en la room destino
if (GameControl::enter_editor) { GameControl::enter_editor(); }

View File

@@ -109,9 +109,6 @@ class MapEditor {
void handleMouseUp();
void updateDrag();
void autosave();
auto getAssetsYamlPath() -> std::string;
void addRoomToAssetsYaml(const std::string& room_name);
void removeRoomFromAssetsYaml(const std::string& room_name);
void updateStatusBarInfo();
static auto snapToGrid(float value) -> float;
static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool;