#include "resource.h" #include // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError, SDL_SetRenderDrawColor, SDL_EventType, SDL_PollEvent, SDL_RenderFillRect, SDL_RenderRect, SDLK_ESCAPE, SDL_Event #include // Para find_if, max #include // Para array #include // Para exit #include // Para runtime_error #include // Para move #include "asset.h" // Para Asset, AssetType #include "color.h" // Para Color #ifndef NO_AUDIO #include "external/jail_audio.h" // Para JA_DeleteMusic, JA_DeleteSound, JA_LoadMusic, JA_LoadSound #endif #include "lang.h" // Para getText #include "param.h" // Para Param, param, ParamResource, ParamGame #include "screen.h" // Para Screen #include "text.h" // Para Text, loadTextFile, TextFile (ptr only) struct JA_Music_t; // lines 11-11 struct JA_Sound_t; // lines 12-12 // Singleton Resource *Resource::instance = nullptr; // Inicializa la instancia única del singleton void Resource::init() { Resource::instance = new Resource(); } // Libera la instancia void Resource::destroy() { delete Resource::instance; } // Obtiene la instancia auto Resource::get() -> Resource * { return Resource::instance; } // Constructor Resource::Resource() : loading_text_(Screen::get()->getText()) { load(); } // Destructor Resource::~Resource() { clear(); } // Vacia todos los vectores de recursos void Resource::clear() { #ifndef NO_AUDIO clearSounds(); clearMusics(); #endif textures_.clear(); text_files_.clear(); texts_.clear(); animations_.clear(); demos_.clear(); } // Carga todos los recursos del juego y muestra el progreso de carga void Resource::load() { // Prepara la gestión del progreso de carga calculateTotalResources(); initProgressBar(); // Muerstra la ventana y desactiva el sincronismo vertical auto *screen = Screen::get(); auto vsync = Screen::getVSync(); screen->setVSync(false); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** LOADING RESOURCES"); #ifndef NO_AUDIO loadSounds(); // Carga sonidos loadMusics(); // Carga músicas #endif loadTextures(); // Carga texturas loadTextFiles(); // Carga ficheros de texto loadAnimations(); // Carga animaciones loadDemoData(); // Carga datos de demo addPalettes(); // Añade paletas a las texturas createText(); // Crea objetos de texto createTextures(); // Crea texturas a partir de texto SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** RESOURCES LOADED"); // Restablece el sincronismo vertical a su valor original screen->setVSync(vsync); } // Recarga todos los recursos (limpia y vuelve a cargar) void Resource::reload() { clear(); load(); } // Recarga solo las texturas y paletas void Resource::reloadTextures() { loadTextures(); addPalettes(); createTextures(); } // Obtiene el sonido a partir de un nombre. Lanza excepción si no existe. auto Resource::getSound(const std::string &name) -> JA_Sound_t * { auto it = std::find_if(sounds_.begin(), sounds_.end(), [&name](const auto &s) { return s.name == name; }); if (it != sounds_.end()) { return it->sound; } SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Sonido no encontrado %s", name.c_str()); throw std::runtime_error("Sonido no encontrado: " + name); } // Obtiene la música a partir de un nombre. Lanza excepción si no existe. auto Resource::getMusic(const std::string &name) -> JA_Music_t * { auto it = std::find_if(musics_.begin(), musics_.end(), [&name](const auto &m) { return m.name == name; }); if (it != musics_.end()) { return it->music; } SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Música no encontrada %s", name.c_str()); throw std::runtime_error("Música no encontrada: " + name); } // Obtiene la textura a partir de un nombre. Lanza excepción si no existe. auto Resource::getTexture(const std::string &name) -> std::shared_ptr { auto it = std::find_if(textures_.begin(), textures_.end(), [&name](const auto &t) { return t.name == name; }); if (it != textures_.end()) { return it->texture; } SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Imagen no encontrada %s", name.c_str()); throw std::runtime_error("Imagen no encontrada: " + name); } // Obtiene el fichero de texto a partir de un nombre. Lanza excepción si no existe. auto Resource::getTextFile(const std::string &name) -> std::shared_ptr { auto it = std::find_if(text_files_.begin(), text_files_.end(), [&name](const auto &t) { return t.name == name; }); if (it != text_files_.end()) { return it->text_file; } SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: TextFile no encontrado %s", name.c_str()); throw std::runtime_error("TextFile no encontrado: " + name); } // Obtiene el objeto de texto a partir de un nombre. Lanza excepción si no existe. auto Resource::getText(const std::string &name) -> std::shared_ptr { auto it = std::find_if(texts_.begin(), texts_.end(), [&name](const auto &t) { return t.name == name; }); if (it != texts_.end()) { return it->text; } SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Text no encontrado %s", name.c_str()); throw std::runtime_error("Text no encontrado: " + name); } // Obtiene la animación a partir de un nombre. Lanza excepción si no existe. auto Resource::getAnimation(const std::string &name) -> AnimationsFileBuffer & { auto it = std::find_if(animations_.begin(), animations_.end(), [&name](const auto &a) { return a.name == name; }); if (it != animations_.end()) { return it->animation; } SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Animación no encontrada %s", name.c_str()); throw std::runtime_error("Animación no encontrada: " + name); } // Obtiene el fichero con los datos para el modo demostración a partir de un índice auto Resource::getDemoData(int index) -> DemoData & { return demos_.at(index); } // Carga los sonidos del juego void Resource::loadSounds() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> SOUND FILES"); auto list = Asset::get()->getListByType(AssetType::SOUND); sounds_.clear(); for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); #ifndef NO_AUDIO sounds_.emplace_back(name, JA_LoadSound(l.c_str())); #endif printWithDots("Sound : ", name, "[ LOADED ]"); } } // Carga las músicas del juego void Resource::loadMusics() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> MUSIC FILES"); auto list = Asset::get()->getListByType(AssetType::MUSIC); musics_.clear(); for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); #ifndef NO_AUDIO musics_.emplace_back(name, JA_LoadMusic(l.c_str())); #endif printWithDots("Music : ", name, "[ LOADED ]"); } } // Carga las texturas del juego void Resource::loadTextures() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> TEXTURES"); auto list = Asset::get()->getListByType(AssetType::BITMAP); textures_.clear(); for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); textures_.emplace_back(name, std::make_shared(Screen::get()->getRenderer(), l)); } } // Carga los ficheros de texto del juego void Resource::loadTextFiles() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> TEXT FILES"); auto list = Asset::get()->getListByType(AssetType::FONT); text_files_.clear(); for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); text_files_.emplace_back(name, loadTextFile(l)); } } // Carga las animaciones del juego void Resource::loadAnimations() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> ANIMATIONS"); auto list = Asset::get()->getListByType(AssetType::ANIMATION); animations_.clear(); for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); animations_.emplace_back(name, loadAnimationsFromFile(l)); } } // Carga los datos para el modo demostración void Resource::loadDemoData() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> DEMO FILES"); constexpr std::array DEMO_FILES = {"demo1.bin", "demo2.bin"}; for (const auto &file : DEMO_FILES) { updateLoadingProgress(file); demos_.emplace_back(loadDemoDataFromFile(Asset::get()->get(file))); } } // Añade paletas de colores a las texturas principales void Resource::addPalettes() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> PALETTES"); // Paletas para el jugador 1 getTexture("player1.gif")->addPaletteFromPalFile(Asset::get()->get("player1_coffee1.pal")); getTexture("player1.gif")->addPaletteFromPalFile(Asset::get()->get("player1_coffee2.pal")); getTexture("player1.gif")->addPaletteFromPalFile(Asset::get()->get("player1_invencible.pal")); // Paletas para el jugador 2 getTexture("player2.gif")->addPaletteFromPalFile(Asset::get()->get("player2_coffee1.pal")); getTexture("player2.gif")->addPaletteFromPalFile(Asset::get()->get("player2_coffee2.pal")); getTexture("player2.gif")->addPaletteFromPalFile(Asset::get()->get("player2_invencible.pal")); } // Crea texturas a partir de textos para mostrar puntuaciones y mensajes void Resource::createTextures() { struct NameAndText { std::string name; std::string text; NameAndText(std::string name_init, std::string text_init) : name(std::move(name_init)), text(std::move(text_init)) {} }; SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXTURES"); // Texturas de tamaño normal std::vector strings = { {"game_text_1000_points", "1.000"}, {"game_text_2500_points", "2.500"}, {"game_text_5000_points", "5.000"}, {"game_text_powerup", Lang::getText("[GAME_TEXT] 4")}, {"game_text_one_hit", Lang::getText("[GAME_TEXT] 5")}, {"game_text_stop", Lang::getText("[GAME_TEXT] 6")}, {"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}}; auto text = getText("04b_25"); for (const auto &s : strings) { textures_.emplace_back(s.name, text->writeToTexture(s.text, 1, -2)); printWithDots("Texture : ", s.name, "[ DONE ]"); } // Texturas de tamaño doble std::vector strings2_x = { {"game_text_100000_points", "100.000"}, {"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")}, {"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")}, {"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")}, {"game_text_game_over", "Game Over"}}; auto text2 = getText("04b_25_2x"); for (const auto &s : strings2_x) { textures_.emplace_back(s.name, text2->writeToTexture(s.text, 1, -4)); printWithDots("Texture : ", s.name, "[ DONE ]"); } } // Crea los objetos de texto a partir de los archivos de textura y texto void Resource::createText() { struct ResourceInfo { std::string key; std::string texture_file; std::string text_file; 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)) {} }; SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXT OBJECTS"); std::vector resources = { {"04b_25", "04b_25.png", "04b_25.txt"}, {"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"}, {"04b_25_metal", "04b_25_metal.png", "04b_25.txt"}, {"04b_25_grey", "04b_25_grey.png", "04b_25.txt"}, {"04b_25_flat", "04b_25_flat.png", "04b_25.txt"}, {"04b_25_reversed", "04b_25_reversed.png", "04b_25.txt"}, {"04b_25_flat_2x", "04b_25_flat_2x.png", "04b_25_2x.txt"}, {"04b_25_reversed_2x", "04b_25_reversed_2x.png", "04b_25_2x.txt"}, {"8bithud", "8bithud.png", "8bithud.txt"}, {"aseprite", "aseprite.png", "aseprite.txt"}, {"smb2", "smb2.png", "smb2.txt"}, {"smb2_grad", "smb2_grad.png", "smb2.txt"}}; for (const auto &resource : resources) { texts_.emplace_back(resource.key, std::make_shared(getTexture(resource.texture_file), getTextFile(resource.text_file))); printWithDots("Text : ", resource.key, "[ DONE ]"); } } // Vacía el vector de sonidos y libera la memoria asociada void Resource::clearSounds() { for (auto &sound : sounds_) { if (sound.sound != nullptr) { #ifndef NO_AUDIO JA_DeleteSound(sound.sound); #endif sound.sound = nullptr; } } sounds_.clear(); } // Vacía el vector de músicas y libera la memoria asociada void Resource::clearMusics() { for (auto &music : musics_) { if (music.music != nullptr) { #ifndef NO_AUDIO JA_DeleteMusic(music.music); #endif music.music = nullptr; } } musics_.clear(); } // Calcula el número total de recursos a cargar y reinicia el contador de carga void Resource::calculateTotalResources() { const std::array ASSET_TYPES = { AssetType::SOUND, AssetType::MUSIC, AssetType::BITMAP, AssetType::FONT, AssetType::ANIMATION, AssetType::DEMODATA}; size_t total = 0; for (const auto &asset_type : ASSET_TYPES) { auto list = Asset::get()->getListByType(asset_type); total += list.size(); } loading_count_ = ResourceCount(total); } // Muestra el progreso de carga en pantalla (barra y texto) void Resource::renderProgress() { // Obtiene la pantalla y el renderer auto *screen = Screen::get(); auto *renderer = screen->getRenderer(); // Actualiza la lógica principal de la pantalla (input, etc.) screen->coreUpdate(); // Inicia el frame y limpia la pantalla screen->start(); screen->clean(); auto color = param.resource.color; // Dibuja el interior de la barra de progreso SDL_SetRenderDrawColor(renderer, param.resource.color.r, param.resource.color.g, param.resource.color.b, param.resource.color.a); SDL_RenderFillRect(renderer, &loading_full_rect_); // Dibuja el marco de la barra de progreso SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); SDL_RenderRect(renderer, &loading_wired_rect_); // Escribe el texto de carga encima de la barra loading_text_->writeColored( loading_wired_rect_.x, loading_wired_rect_.y - 9, Lang::getText("[RESOURCE] LOADING") + " : " + loading_resource_name_, param.resource.color); // Renderiza el frame en pantalla screen->coreRender(); } // Comprueba los eventos durante la carga (permite salir con ESC o cerrar ventana) void Resource::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, muestra la barra y procesa eventos void Resource::updateLoadingProgress(std::string name) { loading_resource_name_ = name; loading_count_.increase(); updateProgressBar(); renderProgress(); checkEvents(); } // Inicializa los rectangulos que definen la barra de progreso void Resource::initProgressBar() { constexpr float X_PADDING = 20.0F; constexpr float Y_PADDING = 20.0F; constexpr float BAR_HEIGHT = 10.0F; const float BAR_Y_POSITION = param.game.height - BAR_HEIGHT - Y_PADDING; const float WIRED_BAR_WIDTH = param.game.width - (X_PADDING * 2); loading_wired_rect_ = {X_PADDING, BAR_Y_POSITION, WIRED_BAR_WIDTH, BAR_HEIGHT}; const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * loading_count_.getPercentage(); loading_full_rect_ = {X_PADDING, BAR_Y_POSITION, FULL_BAR_WIDTH, BAR_HEIGHT}; } // Actualiza la barra de estado void Resource::updateProgressBar() { loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage(); }