#pragma once #include #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. 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 "corridor_wanderer": // (a) A cada creuament de corredor (edge-detect de travessar la línia // central d'un corredor perpendicular) amarra la caixa al centre // exacte del creuament i decideix: seguir recta o girar a una // perpendicular lliure. Mai mitja volta. // (b) Si el pròxim tram la bloqueja (mur del playfield o sarc), gira // cap a una perpendicular vàlida i, només si no en queda cap, fa // mitja volta. // En ambdós casos el frame de gir no consumeix avenç extra. 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); // Si aquest frame travessa la línia central d'un corredor perpendicular, // retorna el delta (sobre l'eix actiu) per amarrar la caixa a aquesta // línia. `nullopt` si no en creua cap. Els corredors perpendiculars // estan a `base + k·pitch` (k ∈ [0..max_idx]); la comparació és // estricta a un costat per no re-disparar quan la caixa ja hi està. [[nodiscard]] auto detectLaneCrossing(float dx, float dy) const -> std::optional; // Decisió a un creuament: pool = perpendiculars lliures + direcció // actual si continua essent lliure. Mai mitja volta. Si el pool sortís // buit (cas degenerat, a la pràctica no s'hi arriba), manté la // direcció actual; el frame següent, si està bloquejada, la branca de // bloqueig ja la reorientarà. void decideAtIntersection(float step_px); // Direcció actual com a enum, reconstruïda a partir de `dir_x_/dir_y_`. // S'usa al pool de creuament per afegir la recta com a candidata. [[nodiscard]] auto currentDirection() const -> Direction; // 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_; };