#include "core/resources/resource_cache.hpp" #include #include // Para find_if #include // Para exit, size_t #include // Para basic_ostream, operator<<, endl, cout #include // Para runtime_error #include #include "core/audio/jail_audio.hpp" // Para JA_DeleteMusic, JA_DeleteSound, JA_Loa... #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/text.hpp" // Para Text, loadTextFile #include "core/resources/resource_helper.hpp" // Para Helper #include "core/resources/resource_list.hpp" // Para List, List::Type #include "game/defaults.hpp" // Para GameDefaults::VERSION #include "game/gameplay/room.hpp" // Para RoomData, loadRoomFile, loadRoomTileFile #include "game/options.hpp" // Para Options, OptionsGame, options #include "utils/defines.hpp" // Para WINDOW_CAPTION #include "utils/utils.hpp" // Para getFileName, printWithDots, PaletteColor #include "version.h" // Para Version::GIT_HASH struct JA_Music_t; // lines 17-17 struct JA_Sound_t; // lines 18-18 namespace Resource { // [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado Cache* Cache::cache = nullptr; // [SINGLETON] Crearemos el objeto cache con esta función estática void Cache::init() { Cache::cache = new Cache(); } // [SINGLETON] Destruiremos el objeto cache con esta función estática void Cache::destroy() { delete Cache::cache; } // [SINGLETON] Con este método obtenemos el objeto cache y podemos trabajar con él auto Cache::get() -> Cache* { return Cache::cache; } // Constructor Cache::Cache() : loading_text_(Screen::get()->getText()) { load(); } // Vacia todos los vectores de recursos void Cache::clear() { clearSounds(); clearMusics(); surfaces_.clear(); palettes_.clear(); text_files_.clear(); texts_.clear(); animations_.clear(); } // Carga todos los recursos void Cache::load() { calculateTotal(); Screen::get()->setBorderColor(static_cast(PaletteColor::BLACK)); std::cout << "** LOADING RESOURCES" << '\n'; loadSounds(); loadMusics(); loadSurfaces(); loadPalettes(); loadTextFiles(); loadAnimations(); loadTileMaps(); loadRooms(); createText(); std::cout << "\n** RESOURCES LOADED" << '\n'; } // Recarga todos los recursos void Cache::reload() { clear(); load(); } // Obtiene el sonido a partir de un nombre auto Cache::getSound(const std::string& name) -> JA_Sound_t* { auto it = std::ranges::find_if(sounds_, [&name](const auto& s) { return s.name == name; }); if (it != sounds_.end()) { return it->sound; } std::cerr << "Error: Sonido no encontrado " << name << '\n'; throw std::runtime_error("Sonido no encontrado: " + name); } // Obtiene la música a partir de un nombre auto Cache::getMusic(const std::string& name) -> JA_Music_t* { auto it = std::ranges::find_if(musics_, [&name](const auto& m) { return m.name == name; }); if (it != musics_.end()) { return it->music; } std::cerr << "Error: Música no encontrada " << name << '\n'; throw std::runtime_error("Música no encontrada: " + name); } // Obtiene la surface a partir de un nombre auto Cache::getSurface(const std::string& name) -> std::shared_ptr { auto it = std::ranges::find_if(surfaces_, [&name](const auto& t) { return t.name == name; }); if (it != surfaces_.end()) { return it->surface; } std::cerr << "Error: Imagen no encontrada " << name << '\n'; throw std::runtime_error("Imagen no encontrada: " + name); } // Obtiene la paleta a partir de un nombre auto Cache::getPalette(const std::string& name) -> Palette { auto it = std::ranges::find_if(palettes_, [&name](const auto& t) { return t.name == name; }); if (it != palettes_.end()) { return it->palette; } std::cerr << "Error: Paleta no encontrada " << name << '\n'; throw std::runtime_error("Paleta no encontrada: " + name); } // Obtiene el fichero de texto a partir de un nombre auto Cache::getTextFile(const std::string& name) -> std::shared_ptr { auto it = std::ranges::find_if(text_files_, [&name](const auto& t) { return t.name == name; }); if (it != text_files_.end()) { return it->text_file; } std::cerr << "Error: TextFile no encontrado " << name << '\n'; throw std::runtime_error("TextFile no encontrado: " + name); } // Obtiene el objeto de texto a partir de un nombre auto Cache::getText(const std::string& name) -> std::shared_ptr { auto it = std::ranges::find_if(texts_, [&name](const auto& t) { return t.name == name; }); if (it != texts_.end()) { return it->text; } std::cerr << "Error: Text no encontrado " << name << '\n'; throw std::runtime_error("Texto no encontrado: " + name); } // Obtiene la animación a partir de un nombre auto Cache::getAnimations(const std::string& name) -> SurfaceAnimatedSprite::Animations& { auto it = std::ranges::find_if(animations_, [&name](const auto& a) { return a.name == name; }); if (it != animations_.end()) { return it->animation; } std::cerr << "Error: Animación no encontrada " << name << '\n'; throw std::runtime_error("Animación no encontrada: " + name); } // Obtiene el mapa de tiles a partir de un nombre auto Cache::getTileMap(const std::string& name) -> std::vector& { auto it = std::ranges::find_if(tile_maps_, [&name](const auto& t) { return t.name == name; }); if (it != tile_maps_.end()) { return it->tile_map; } std::cerr << "Error: Mapa de tiles no encontrado " << name << '\n'; throw std::runtime_error("Mapa de tiles no encontrado: " + name); } // Obtiene la habitación a partir de un nombre auto Cache::getRoom(const std::string& name) -> std::shared_ptr { auto it = std::ranges::find_if(rooms_, [&name](const auto& r) { return r.name == name; }); if (it != rooms_.end()) { return it->room; } std::cerr << "Error: Habitación no encontrada " << name << '\n'; throw std::runtime_error("Habitación no encontrada: " + name); } // Obtiene todas las habitaciones auto Cache::getRooms() -> std::vector& { return rooms_; } // Carga los sonidos void Cache::loadSounds() { std::cout << "\n>> SOUND FILES" << '\n'; auto list = List::get()->getListByType(List::Type::SOUND); sounds_.clear(); for (const auto& l : list) { auto name = getFileName(l); JA_Sound_t* sound = nullptr; // Try loading from resource pack first auto audio_data = Helper::loadFile(l); if (!audio_data.empty()) { sound = JA_LoadSound(audio_data.data(), static_cast(audio_data.size())); } // Fallback to file path if memory loading failed if (sound == nullptr) { sound = JA_LoadSound(l.c_str()); } sounds_.emplace_back(name, sound); printWithDots("Sound : ", name, "[ LOADED ]"); updateLoadingProgress(); } } // Carga las musicas void Cache::loadMusics() { std::cout << "\n>> MUSIC FILES" << '\n'; auto list = List::get()->getListByType(List::Type::MUSIC); musics_.clear(); for (const auto& l : list) { auto name = getFileName(l); JA_Music_t* music = nullptr; // Try loading from resource pack first auto audio_data = Helper::loadFile(l); if (!audio_data.empty()) { music = JA_LoadMusic(audio_data.data(), static_cast(audio_data.size())); } // Fallback to file path if memory loading failed if (music == nullptr) { music = JA_LoadMusic(l.c_str()); } musics_.emplace_back(name, music); printWithDots("Music : ", name, "[ LOADED ]"); updateLoadingProgress(1); } } // Carga las texturas void Cache::loadSurfaces() { std::cout << "\n>> SURFACES" << '\n'; auto list = List::get()->getListByType(List::Type::BITMAP); surfaces_.clear(); for (const auto& l : list) { auto name = getFileName(l); surfaces_.emplace_back(name, std::make_shared(l)); surfaces_.back().surface->setTransparentColor(0); updateLoadingProgress(); } // Reconfigura el color transparente de algunas surfaces getSurface("loading_screen_color.gif")->setTransparentColor(); getSurface("ending1.gif")->setTransparentColor(); getSurface("ending2.gif")->setTransparentColor(); getSurface("ending3.gif")->setTransparentColor(); getSurface("ending4.gif")->setTransparentColor(); getSurface("ending5.gif")->setTransparentColor(); getSurface("standard.gif")->setTransparentColor(16); } // Carga las paletas void Cache::loadPalettes() { std::cout << "\n>> PALETTES" << '\n'; auto list = List::get()->getListByType(List::Type::PALETTE); palettes_.clear(); for (const auto& l : list) { auto name = getFileName(l); palettes_.emplace_back(name, readPalFile(l)); updateLoadingProgress(); } } // Carga los ficheros de texto void Cache::loadTextFiles() { std::cout << "\n>> TEXT FILES" << '\n'; auto list = List::get()->getListByType(List::Type::FONT); text_files_.clear(); for (const auto& l : list) { auto name = getFileName(l); text_files_.emplace_back(name, Text::loadTextFile(l)); updateLoadingProgress(); } } // Carga las animaciones void Cache::loadAnimations() { std::cout << "\n>> ANIMATIONS" << '\n'; auto list = List::get()->getListByType(List::Type::ANIMATION); animations_.clear(); for (const auto& l : list) { auto name = getFileName(l); animations_.emplace_back(name, SurfaceAnimatedSprite::loadAnimationsFromFile(l)); updateLoadingProgress(); } } // Carga los mapas de tiles // DEPRECATED: Los tilemaps ahora vienen embebidos en los archivos YAML de habitaciones void Cache::loadTileMaps() { std::cout << "\n>> TILE MAPS (skipped - embebidos en YAML)" << '\n'; // Ya no se cargan tilemaps por separado } // Carga las habitaciones desde archivos YAML void Cache::loadRooms() { std::cout << "\n>> ROOMS" << '\n'; auto list = List::get()->getListByType(List::Type::ROOM); rooms_.clear(); for (const auto& l : list) { auto name = getFileName(l); rooms_.emplace_back(name, std::make_shared(Room::loadYAML(l))); printWithDots("Room : ", name, "[ LOADED ]"); updateLoadingProgress(); } } void Cache::createText() { struct ResourceInfo { std::string key; // Identificador del recurso std::string texture_file; // Nombre del archivo de textura std::string text_file; // Nombre del archivo de texto // Constructor para facilitar la creación de objetos ResourceInfo ResourceInfo(std::string k, std::string t_file, std::string txt_file) : key(std::move(k)), texture_file(std::move(t_file)), text_file(std::move(txt_file)) {} }; std::cout << "\n>> CREATING TEXT_OBJECTS" << '\n'; std::vector resources = { {"aseprite", "aseprite.gif", "aseprite.txt"}, {"gauntlet", "gauntlet.gif", "gauntlet.txt"}, {"smb2", "smb2.gif", "smb2.txt"}, {"subatomic", "subatomic.gif", "subatomic.txt"}, {"8bithud", "8bithud.gif", "8bithud.txt"}}; for (const auto& res_info : resources) { texts_.emplace_back(res_info.key, std::make_shared(getSurface(res_info.texture_file), getTextFile(res_info.text_file))); printWithDots("Text : ", res_info.key, "[ DONE ]"); } } // Vacía el vector de sonidos void Cache::clearSounds() { // Itera sobre el vector y libera los recursos asociados a cada JA_Sound_t for (auto& sound : sounds_) { if (sound.sound != nullptr) { JA_DeleteSound(sound.sound); sound.sound = nullptr; } } sounds_.clear(); // Limpia el vector después de liberar todos los recursos } // Vacía el vector de musicas void Cache::clearMusics() { // Itera sobre el vector y libera los recursos asociados a cada JA_Music_t for (auto& music : musics_) { if (music.music != nullptr) { JA_DeleteMusic(music.music); music.music = nullptr; } } musics_.clear(); // Limpia el vector después de liberar todos los recursos } // Calcula el numero de recursos para cargar void Cache::calculateTotal() { std::vector asset_types = { List::Type::SOUND, List::Type::MUSIC, List::Type::BITMAP, List::Type::PALETTE, List::Type::FONT, List::Type::ANIMATION, List::Type::ROOM}; size_t total = 0; for (const auto& asset_type : asset_types) { auto list = List::get()->getListByType(asset_type); total += list.size(); } count_ = ResourceCount(total, 0); } // Muestra el progreso de carga void Cache::renderProgress() { constexpr float X_PADDING = 60.0F; constexpr float Y_PADDING = 10.0F; constexpr float BAR_HEIGHT = 5.0F; const float BAR_POSITION = Options::game.height - BAR_HEIGHT - Y_PADDING; Screen::get()->start(); Screen::get()->clearSurface(static_cast(PaletteColor::BLACK)); auto surface = Screen::get()->getRendererSurface(); const auto LOADING_TEXT_COLOR = static_cast(PaletteColor::BRIGHT_WHITE); const auto BAR_COLOR = static_cast(PaletteColor::WHITE); const int TEXT_HEIGHT = loading_text_->getCharacterSize(); const int CENTER_X = Options::game.width / 2; const int CENTER_Y = Options::game.height / 2; // Draw APP_NAME centered above center const std::string APP_NAME = spaceBetweenLetters(Version::APP_NAME); loading_text_->writeColored( CENTER_X - (loading_text_->length(APP_NAME) / 2), CENTER_Y - TEXT_HEIGHT, APP_NAME, LOADING_TEXT_COLOR); // Draw VERSION centered below center const std::string VERSION_TEXT = "(" + std::string(Version::GIT_HASH) + ")"; loading_text_->writeColored( CENTER_X - (loading_text_->length(VERSION_TEXT) / 2), CENTER_Y + TEXT_HEIGHT, VERSION_TEXT, LOADING_TEXT_COLOR); // Draw progress bar border const float WIRED_BAR_WIDTH = Options::game.width - (X_PADDING * 2); SDL_FRect rect_wired = {X_PADDING, BAR_POSITION, WIRED_BAR_WIDTH, BAR_HEIGHT}; surface->drawRectBorder(&rect_wired, BAR_COLOR); // Draw progress bar fill const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * count_.getPercentage(); SDL_FRect rect_full = {X_PADDING, BAR_POSITION, FULL_BAR_WIDTH, BAR_HEIGHT}; surface->fillRect(&rect_full, BAR_COLOR); Screen::get()->render(); } // Comprueba los eventos de la pantalla de carga void Cache::checkEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_EVENT_QUIT: exit(0); break; case SDL_EVENT_KEY_DOWN: if (event.key.key == SDLK_ESCAPE) { exit(0); } break; } } } // Actualiza el progreso de carga void Cache::updateLoadingProgress(int steps) { count_.add(1); if (count_.loaded % steps == 0 || count_.loaded == count_.total) { renderProgress(); } checkEvents(); } } // namespace Resource