#pragma once #include #include // Component reutilitzable d'animació: qualsevol entitat que necessiti // reproduir una seqüència de frames l'instancia. Es manté com a camp // opcional dins d'Entity (veure `entity.hpp`) i NO hereta de cap classe // del pipeline de sprites existent: això manté les entitats estàtiques // (hazards invisibles, triggers) lliures de l'estat d'animació. // // Els frames s'expressen com a índexs enters dins d'un spritesheet. Quan // algun dia aterri l'art definitiu, el renderer consultarà `currentFrame()` // i traduirà l'índex al rectangle del sprite corresponent. De moment els // placeholders tenen un únic frame i `currentFrame()` retorna sempre 0. class AnimationComponent { public: AnimationComponent() = default; AnimationComponent(std::vector frames, float fps, bool loop); // Avanç del temps. Si `frames_` està buit o té un sol element, no fa // res. En mode loop, `current_frame_` rebota a 0 en passar del final; // en mode no-loop, es queda al darrer frame i `completed()` passa a // true. void tick(float delta_time); // Torna al primer frame i reinicia l'acumulador. Útil en transicions // d'estat (p.ex. Sarcòfag: CLOSED → OPENING reinicia l'animació). void reset(); // Índex actual dins `frames_`. Si `frames_` està buit retorna 0 com // a fallback tolerant (el loader ja ha emès warning si hi havia // frames fora de rang; aquí preferim no crashejar). [[nodiscard]] auto currentFrame() const -> int; [[nodiscard]] auto frameCount() const -> std::size_t { return frames_.size(); } [[nodiscard]] auto completed() const -> bool { return completed_; } [[nodiscard]] auto loops() const -> bool { return loop_; } private: std::vector frames_; float fps_{1.0F}; bool loop_{true}; int current_{0}; float accumulator_{0.0F}; bool completed_{false}; };