#include "animated_sprite.h" #include // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError #include // Para min #include // Para size_t #include // Para basic_istream, basic_ifstream, basic_ios, ifst... #include // Para basic_stringstream #include // Para runtime_error #include // Para pair #include "texture.h" // Para Texture #include "utils.h" // Para printWithDots // Carga las animaciones en un vector(Animations) desde un fichero auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer { std::ifstream file(file_path); if (!file) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str()); throw std::runtime_error("Fichero no encontrado: " + file_path); } printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]"); std::vector buffer; std::string line; while (std::getline(file, line)) { if (!line.empty()) { buffer.push_back(line); } } return buffer; } // Constructor AnimatedSprite::AnimatedSprite(std::shared_ptr texture, const std::string& file_path) : MovingSprite(texture) { // Carga las animaciones if (!file_path.empty()) { auto buffer = loadAnimationsFromFile(file_path); loadFromAnimationsFileBuffer(buffer); } } // Constructor AnimatedSprite::AnimatedSprite(std::shared_ptr texture, const AnimationsFileBuffer& animations) : MovingSprite(texture) { if (!animations.empty()) { loadFromAnimationsFileBuffer(animations); } } // Obtiene el índice de la animación a partir del nombre auto AnimatedSprite::getIndex(const std::string& name) -> int { auto iterator = animation_indices_.find(name); if (iterator != animation_indices_.end()) { // Si se encuentra la animación en el mapa, devuelve su índice return iterator->second; } // Si no se encuentra, muestra una advertencia y devuelve -1 SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "** Warning: could not find \"%s\" animation", name.c_str()); 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 >= 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++; } } // Comprueba si ha terminado la animación auto AnimatedSprite::animationIsCompleted() -> bool { return animations_[current_animation_].completed; } // Establece la animacion actual void AnimatedSprite::setCurrentAnimation(const std::string& name, bool reset) { const auto NEW_ANIMATION = getIndex(name); if (current_animation_ != NEW_ANIMATION) { const auto OLD_ANIMATION = current_animation_; current_animation_ = NEW_ANIMATION; if (reset) { animations_[current_animation_].current_frame = 0; animations_[current_animation_].counter = 0; animations_[current_animation_].completed = false; } else { animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size()); animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; } } } // Establece la animacion actual void AnimatedSprite::setCurrentAnimation(int index, bool reset) { const auto NEW_ANIMATION = index; if (current_animation_ != NEW_ANIMATION) { const auto OLD_ANIMATION = current_animation_; current_animation_ = NEW_ANIMATION; if (reset) { animations_[current_animation_].current_frame = 0; animations_[current_animation_].counter = 0; animations_[current_animation_].completed = false; } else { animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size()); animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; } } } // Actualiza las variables del objeto void AnimatedSprite::update() { animate(); MovingSprite::update(); } // 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 vector de cadenas void AnimatedSprite::loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source) { AnimationConfig config; size_t index = 0; while (index < source.size()) { const std::string& line = source.at(index); if (line == "[animation]") { index = processAnimationBlock(source, index, config); } else { processConfigLine(line, config); } index++; } // Pone un valor por defecto setWidth(config.frame_width); setHeight(config.frame_height); } // Procesa una línea de configuración void AnimatedSprite::processConfigLine(const std::string& line, AnimationConfig& config) { size_t pos = line.find("="); if (pos == std::string::npos) { return; } std::string key = line.substr(0, pos); int value = std::stoi(line.substr(pos + 1)); if (key == "frame_width") { config.frame_width = value; updateFrameCalculations(config); } else if (key == "frame_height") { config.frame_height = value; updateFrameCalculations(config); } else { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str()); } } // Actualiza los cálculos basados en las dimensiones del frame void AnimatedSprite::updateFrameCalculations(AnimationConfig& config) { config.frames_per_row = texture_->getWidth() / config.frame_width; const int WIDTH = texture_->getWidth() / config.frame_width; const int HEIGHT = texture_->getHeight() / config.frame_height; config.max_tiles = WIDTH * HEIGHT; } // Procesa un bloque completo de animación auto AnimatedSprite::processAnimationBlock(const AnimationsFileBuffer& source, size_t start_index, const AnimationConfig& config) -> size_t { Animation animation; size_t index = start_index + 1; // Salta la línea "[animation]" while (index < source.size()) { const std::string& line = source.at(index); if (line == "[/animation]") { break; } processAnimationParameter(line, animation, config); index++; } // Añade la animación al vector de animaciones animations_.emplace_back(animation); // Rellena el mapa con el nombre y el nuevo índice animation_indices_[animation.name] = animations_.size() - 1; return index; } // Procesa un parámetro individual de animación void AnimatedSprite::processAnimationParameter(const std::string& line, Animation& animation, const AnimationConfig& config) { size_t pos = line.find("="); if (pos == std::string::npos) { return; } std::string key = line.substr(0, pos); std::string value = line.substr(pos + 1); if (key == "name") { animation.name = value; } else if (key == "speed") { animation.speed = std::stoi(value); } else if (key == "loop") { animation.loop = std::stoi(value); } else if (key == "frames") { parseFramesParameter(value, animation, config); } else { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str()); } } // Parsea el parámetro de frames (lista separada por comas) void AnimatedSprite::parseFramesParameter(const std::string& frames_str, Animation& animation, const AnimationConfig& config) { std::stringstream stream(frames_str); std::string tmp; SDL_FRect rect = {0, 0, config.frame_width, config.frame_height}; while (getline(stream, tmp, ',')) { const int NUM_TILE = std::stoi(tmp); if (NUM_TILE <= config.max_tiles) { rect.x = (NUM_TILE % config.frames_per_row) * config.frame_width; rect.y = (NUM_TILE / config.frames_per_row) * config.frame_height; animation.frames.emplace_back(rect); } } } // Establece la velocidad de la animación void AnimatedSprite::setAnimationSpeed(size_t value) { animations_[current_animation_].speed = value; }