#pragma once #include #include #include #include #include "external/fkyaml_node.hpp" #include "game/entities/animation_component.hpp" #include "game/entities/attack.hpp" class Entity; // Atac de látigo: spawnejat pel jugador en entrar a `ATTACKING`. Viu la // durada de la seua animació (`swing`) i s'autoelimina al final. // // Sprites: 4 fitxers GIF distints, un per cardinal. La mida del frame // canvia entre direccions (E/W: 48×16, N/S: 16×48), cosa que el sistema // d'animació actual no permet expressar amb una sola surface (una entitat // = una graella). Per això el YAML llista les 4 entrades sota `sprites:` // i `configure()` tria la que toca segons direcció. // // Hitbox de dany: el rectangle de dany és el `pixel_rect_` retallat al // primer bloquejador en la línia d'atac (mur extern del playfield o // sarcòfag). Aquesta lògica viu a la fase de damage application; en // aquest skeleton només viu la part visual i de cicle de vida. class WhipAttack : public Attack { public: // Direcció cardinal del swing. Coincideix valors amb // `Player::Cardinal` (1=N, 2=S, 3=E, 4=W) per facilitar la // conversió a la frontera de l'event `whip_spawn`. enum class Direction : std::uint8_t { NORTH = 1, SOUTH = 2, EAST = 3, WEST = 4, }; // Definició estàtica d'una direcció: clau al `Resource::Cache` i mida // del frame en píxels. Llegida del YAML una vegada al ctor; usada per // configure() per omplir surface_ i pixel_rect_. Pública perquè el // helper d'anàlisi del YAML viu en un namespace anònim al .cpp. struct DirSprite { std::string surface_key; float frame_w{0.0F}; float frame_h{0.0F}; }; explicit WhipAttack(const fkyaml::node& yaml); // Configura l'estat runtime de la instància. Cal cridar-lo abans del // primer `update`. Selecciona la surface i la mida del pixel_rect_ // segons direcció, i posa la cantonada superior-esquerra de la // hitbox a (spawn_x, spawn_y). void configure(Direction dir, float spawn_x, float spawn_y, float range_px); // Avança animació; quan completa, marca dead. void update(float delta_time) override; [[nodiscard]] auto getDirection() const -> Direction { return direction_; } // True si el frame actual de l'animació és un dels `active_frames` // declarats al YAML (els frames durant els quals la hitbox aplica // dany). Wind-up i recovery retornen false. [[nodiscard]] auto isFrameActive() const -> bool; // Llistat dels frames actius (lectura del YAML, immutable per // instància). Exposat per als smoke tests. [[nodiscard]] auto getActiveFrames() const -> const std::vector& { return active_frames_; } // Llargada de la hitbox abans de retallar contra murs/sarcs (en píxels). [[nodiscard]] auto getRangePx() const -> float { return range_px_; } // Clau al `Resource::Cache` per a la surface de la direcció actual // (la que ha triat `configure()`). El caller (típicament `GameScene`) // resol la surface i la passa via `setSurface()`. Aquesta separació // manté el codi de l'entitat sense dependència directa al // `Resource::Cache`, igual que la resta d'entitats del joc. [[nodiscard]] auto getSurfaceKey() const -> const std::string&; private: [[nodiscard]] auto spriteFor(Direction dir) const -> const DirSprite&; Direction direction_{Direction::EAST}; AnimationComponent anim_swing_; std::vector active_frames_; std::set already_hit_; float range_px_{0.0F}; DirSprite sprite_north_; DirSprite sprite_south_; DirSprite sprite_east_; DirSprite sprite_west_; };