#include "animated_sprite.h" #include // for copy #include // for basic_ostream, operator<<, basic_istream, basic... #include // for cout #include // for back_insert_iterator, back_inserter #include // for basic_stringstream #include "texture.h" // for Texture #include "utils.h" // Carga las animaciones en un vector(Animations) desde un fichero Animations loadAnimationsFromFile(const std::string &file_path) { std::vector buffer; std::ifstream file(file_path); if (!file) { std::cerr << "Error: Fichero no encontrado " << file_path << std::endl; throw std::runtime_error("Fichero no encontrado: " + file_path); } std::string line; printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]"); while (std::getline(file, line)) { buffer.push_back(line); } return buffer; } // Constructor AnimatedSprite::AnimatedSprite(std::shared_ptr texture, const std::string &file_path) : MovingSprite(texture), current_animation_(0) { // Carga las animaciones if (!file_path.empty()) { animations_ = loadFromFile(file_path); } } AnimatedSprite::AnimatedSprite(std::shared_ptr texture, const Animations &animations) : MovingSprite(texture), current_animation_(0) { if (!animations.empty()) { loadFromVector(animations); } } // Constructor AnimatedSprite::AnimatedSprite(std::shared_ptr texture) : MovingSprite(texture), current_animation_(0) {} // Obtiene el indice de la animación a partir del nombre int AnimatedSprite::getIndex(const std::string &name) { auto index = -1; for (const auto &a : animations_) { index++; if (a.name == name) { return index; } } std::cout << "** Warning: could not find \"" << name.c_str() << "\" animation" << std::endl; return -1; } // Calcula el frame correspondiente a la animación void AnimatedSprite::animate() { if (animations_[current_animation_].speed == 0) { return; } // Calcula el frame actual a partir del contador animations_[current_animation_].current_frame = animations_[current_animation_].counter / animations_[current_animation_].speed; // Si alcanza el final de la animación, reinicia el contador de la animación // en función de la variable loop y coloca el nuevo frame if (animations_[current_animation_].current_frame >= (int)animations_[current_animation_].frames.size()) { if (animations_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame animations_[current_animation_].current_frame = animations_[current_animation_].frames.size(); animations_[current_animation_].completed = true; } else { // Si hay loop, vuelve al frame indicado animations_[current_animation_].counter = 0; animations_[current_animation_].current_frame = animations_[current_animation_].loop; } } // En caso contrario else { // Escoge el frame correspondiente de la animación setSpriteClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]); // Incrementa el contador de la animacion animations_[current_animation_].counter++; } } // Obtiene el número de frames de la animación actual int AnimatedSprite::getNumFrames() { return (int)animations_[current_animation_].frames.size(); } // Establece el frame actual de la animación void AnimatedSprite::setCurrentFrame(int num) { // Descarta valores fuera de rango if (num >= (int)animations_[current_animation_].frames.size()) { num = 0; } // Cambia el valor de la variable animations_[current_animation_].current_frame = num; animations_[current_animation_].counter = 0; // Escoge el frame correspondiente de la animación setSpriteClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]); } // Establece el valor del contador void AnimatedSprite::setAnimationCounter(const std::string &name, int num) { animations_[getIndex(name)].counter = num; } // Establece la velocidad de una animación void AnimatedSprite::setAnimationSpeed(const std::string &name, int speed) { animations_[getIndex(name)].counter = speed; } // Establece la velocidad de una animación void AnimatedSprite::setAnimationSpeed(int index, int speed) { animations_[index].counter = speed; } // Establece si la animación se reproduce en bucle void AnimatedSprite::setAnimationLoop(const std::string &name, int loop) { animations_[getIndex(name)].loop = loop; } // Establece si la animación se reproduce en bucle void AnimatedSprite::setAnimationLoop(int index, int loop) { animations_[index].loop = loop; } // Establece el valor de la variable void AnimatedSprite::setAnimationCompleted(const std::string &name, bool value) { animations_[getIndex(name)].completed = value; } // OLD - Establece el valor de la variable void AnimatedSprite::setAnimationCompleted(int index, bool value) { animations_[index].completed = value; } // Comprueba si ha terminado la animación bool AnimatedSprite::animationIsCompleted() { return animations_[current_animation_].completed; } // Devuelve el rectangulo de una animación y frame concreto SDL_Rect AnimatedSprite::getAnimationClip(const std::string &name, Uint8 index) { return animations_[getIndex(name)].frames[index]; } // Devuelve el rectangulo de una animación y frame concreto SDL_Rect AnimatedSprite::getAnimationClip(int indexA, Uint8 indexF) { return animations_[indexA].frames[indexF]; } // Establece la animacion actual void AnimatedSprite::setCurrentAnimation(const std::string &name) { const auto new_animation = getIndex(name); if (current_animation_ != new_animation) { current_animation_ = new_animation; animations_[current_animation_].current_frame = 0; animations_[current_animation_].counter = 0; animations_[current_animation_].completed = false; } } // Establece la animacion actual void AnimatedSprite::setCurrentAnimation(int index) { const auto new_animation = index; if (current_animation_ != new_animation) { current_animation_ = new_animation; animations_[current_animation_].current_frame = 0; animations_[current_animation_].counter = 0; animations_[current_animation_].completed = false; } } // Actualiza las variables del objeto void AnimatedSprite::update() { animate(); MovingSprite::update(); } // Establece el rectangulo para un frame de una animación void AnimatedSprite::setAnimationFrames(Uint8 index_animation, Uint8 index_frame, int x, int y, int w, int h) { animations_[index_animation].frames.push_back({x, y, w, h}); } // OLD - Establece el contador para todas las animaciones void AnimatedSprite::setAnimationCounter(int value) { for (auto &a : animations_) { a.counter = value; } } // Reinicia la animación void AnimatedSprite::resetAnimation() { animations_[current_animation_].current_frame = 0; animations_[current_animation_].counter = 0; animations_[current_animation_].completed = false; } // Carga la animación desde un fichero std::vector AnimatedSprite::loadFromFile(const std::string &file_path) { // Inicializa variables std::vector animations; auto frame_width = 1; auto frame_height = 1; std::ifstream file(file_path); std::string line; // El fichero se puede abrir if (file.good()) { // Procesa el fichero linea a linea std::cout << "Animation loaded: " << getFileName(file_path) << std::endl; while (std::getline(file, line)) { auto max_tiles = 1; auto frames_per_row = 1; // Si la linea contiene el texto [animation] se realiza el proceso de carga de una animación if (line == "[animation]") { Animation animation = Animation(); do { std::getline(file, line); // Encuentra la posición del caracter '=' int pos = line.find("="); // Procesa las dos subcadenas if (pos != static_cast(line.npos)) { if (line.substr(0, pos) == "name") { animation.name = line.substr(pos + 1, line.length()); } else if (line.substr(0, pos) == "speed") { animation.speed = std::stoi(line.substr(pos + 1, line.length())); } else if (line.substr(0, pos) == "loop") { animation.loop = std::stoi(line.substr(pos + 1, line.length())); } else if (line.substr(0, pos) == "frames") { // Se introducen los valores separados por comas en un vector std::stringstream ss(line.substr(pos + 1, line.length())); std::string tmp; SDL_Rect rect = {0, 0, frame_width, frame_height}; while (getline(ss, tmp, ',')) { // Comprueba que el tile no sea mayor que el maximo indice permitido const auto num_tile = std::stoi(tmp) > max_tiles ? 0 : std::stoi(tmp); rect.x = (num_tile % frames_per_row) * frame_width; rect.y = (num_tile / frames_per_row) * frame_height; animation.frames.push_back(rect); } } else { std::cout << "Warning: file " << getFileName(file_path).c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl; } } } while (line != "[/animation]"); // Añade la animación al vector de animaciones animations.push_back(animation); } // En caso contrario se parsea el fichero para buscar las variables y los valores if (line != "[animation]") { // Encuentra la posición del caracter '=' size_t pos = line.find("="); // Procesa las dos subcadenas if (pos != line.npos) { if (line.substr(0, pos) == "frame_width") { frame_width = std::stoi(line.substr(pos + 1, line.length())); } else if (line.substr(0, pos) == "frame_height") { frame_height = std::stoi(line.substr(pos + 1, line.length())); } else { std::cout << "Warning: file " << getFileName(file_path) << "\n, unknown parameter \"" << line.substr(0, pos) << "\"" << std::endl; } frames_per_row = texture_->getWidth() / frame_width; const auto w = texture_->getWidth() / frame_width; const auto h = texture_->getHeight() / frame_height; max_tiles = w * h; } } } // Cierra el fichero file.close(); } // El fichero no se puede abrir else { std::cout << "Warning: Unable to open " << getFileName(file_path).c_str() << " file" << std::endl; } // Pone un valor por defecto setWidth(frame_width); setHeight(frame_height); return animations; } // Carga la animación desde un vector bool AnimatedSprite::loadFromVector(const Animations &source) { // Inicializa variables auto frames_per_row = 0; auto frame_width = 0; auto frame_height = 0; auto max_tiles = 0; // Indicador de éxito en el proceso auto success = true; std::string line; // Recorre todo el vector auto index = 0; while (index < (int)source.size()) { // Lee desde el vector line = source.at(index); // Si la linea contiene el texto [animation] se realiza el proceso de carga de una animación if (line == "[animation]") { Animation animation = Animation(); do { // Aumenta el indice para leer la siguiente linea index++; line = source.at(index); // Encuentra la posición del caracter '=' int pos = line.find("="); // Procesa las dos subcadenas if (pos != static_cast(line.npos)) { if (line.substr(0, pos) == "name") { animation.name = line.substr(pos + 1, line.length()); } else if (line.substr(0, pos) == "speed") { animation.speed = std::stoi(line.substr(pos + 1, line.length())); } else if (line.substr(0, pos) == "loop") { animation.loop = std::stoi(line.substr(pos + 1, line.length())); } else if (line.substr(0, pos) == "frames") { // Se introducen los valores separados por comas en un vector std::stringstream ss(line.substr(pos + 1, line.length())); std::string tmp; SDL_Rect rect = {0, 0, frame_width, frame_height}; while (getline(ss, tmp, ',')) { // Comprueba que el tile no sea mayor que el maximo indice permitido const int num_tile = std::stoi(tmp) > max_tiles ? 0 : std::stoi(tmp); rect.x = (num_tile % frames_per_row) * frame_width; rect.y = (num_tile / frames_per_row) * frame_height; animation.frames.push_back(rect); } } else { std::cout << "Warning: unknown parameter " << line.substr(0, pos).c_str() << std::endl; success = false; } } } while (line != "[/animation]"); // Añade la animación al vector de animaciones animations_.push_back(animation); } // En caso contrario se parsea el fichero para buscar las variables y los valores else { // Encuentra la posición del caracter '=' int pos = line.find("="); // Procesa las dos subcadenas if (pos != (int)line.npos) { if (line.substr(0, pos) == "frames_per_row") { frames_per_row = std::stoi(line.substr(pos + 1, line.length())); } else if (line.substr(0, pos) == "frame_width") { frame_width = std::stoi(line.substr(pos + 1, line.length())); } else if (line.substr(0, pos) == "frame_height") { frame_height = std::stoi(line.substr(pos + 1, line.length())); } else { std::cout << "Warning: unknown parameter " << line.substr(0, pos).c_str() << std::endl; success = false; } // Normaliza valores if (frames_per_row == 0 && frame_width > 0) { frames_per_row = texture_->getWidth() / frame_width; } if (max_tiles == 0 && frame_width > 0 && frame_height > 0) { const int w = texture_->getWidth() / frame_width; const int h = texture_->getHeight() / frame_height; max_tiles = w * h; } } } // Una vez procesada la linea, aumenta el indice para pasar a la siguiente index++; } // Pone un valor por defecto setWidth(frame_width); setHeight(frame_height); return success; }