#pragma once #include #include #include "external/fkyaml_node.hpp" #include "game/entities/animation_component.hpp" #include "game/entities/hazard.hpp" // Mòmia: perill dinàmic que deambula pels passadís. A cada intersecció // tria una direcció vàlida; l'algoritme per triar-la és un hook virtual // (`chooseDirection`) que la base implementa com a aleatori uniforme // sobre les direccions admissibles. Futurs derivats poden sesgar cap al // jugador sense tocar la lògica de moviment. // // Andamiatge: el concepte d'"intersecció" i la validació de direcció // requereixen conèixer el mapa. De moment l'exposem com un conjunt de // direccions admissibles que l'EntityManager injecta; la implementació // real arribarà quan cablegem l'entitat al game loop. class Mummy : public DynamicHazard { public: enum class Direction : std::uint8_t { NONE, UP, DOWN, LEFT, RIGHT }; explicit Mummy(const fkyaml::node& yaml); // Hook d'IA. Donat un conjunt de direccions admissibles, decideix la // següent direcció a prendre. La implementació de la base tria una // aleatoria; si `allowed` està buit, retorna `NONE`. virtual auto chooseDirection(const std::vector& allowed) -> Direction; // Política de moviment "corridor_wanderer": camina recte fins que el // pròxim tram la bloquejaria (mur del playfield o sarc). Quan troba un // obstacle, recull les direccions perpendiculars vàlides (evitant // U-turn llevat que no hi hagi alternativa) i crida `chooseDirection` // per decidir-ne una. No consumeix moviment el frame de gir. void step(float delta_time) override; // Contacte amb el jugador: publica `player_hit` amb el dany. void onPlayerContact(Player* player) override; private: // Caixa de col·lisió candidata si l'entitat es desplaçara (dx, dy). [[nodiscard]] auto candidateBox(float dx, float dy) const -> SDL_FRect; // Amarra la caja a la vora exacta del playfield quan el candidat hi // sortiria — sense això, amb la caja 16×16 iguala el corredor, la // posició subpíxel dins de l'última cel·la impediria girar a cap // perpendicular (col·lisionaria amb el sarc adjacent). Es crida abans // de la lògica de gir. void snapToPlayfieldEdge(float dx, float dy); // Recull les direccions alternatives viables des de la posició actual: // `perpendiculars` (girs) i `uturn` (inversa). La direcció actual s'exclou // perquè ja sabem que està bloquejada. Cada candidata es valida amb // `Map::insidePlayfield` + `Map::collides` sobre la caja resultant. struct DirectionPools { std::vector perp; std::vector uturn; }; [[nodiscard]] auto collectDirectionPools(float step) const -> DirectionPools; // Canvi de direcció + aplicació de l'animació i flip corresponents. // Tots els camins que trien nova direcció (ctor amb `initial_direction`, // gir després d'un bloqueig) passen per ací per mantenir anim_ i // flip_ en sincronia amb (dir_x_, dir_y_). void changeDirection(float dx, float dy); // Aplica la direcció triada (wrapper del switch en una sola crida). void applyChoice(Direction dir); // Animacions carregades del YAML, una per cada facing. Per a horitzontal // només en cal una (`walk`) i el flip ja diferencia esquerra/dreta. AnimationComponent anim_walk_down_; AnimationComponent anim_walk_up_; AnimationComponent anim_walk_lateral_; };