Files
aee-arcade/source/game/entities/concrete/mummy.hpp
T
JailDesigner 9d256b7f2d feat(mummy): decisió a creuaments de corredor
La mòmia ara decideix direcció a cada creuament de corredor (edge-detect
de travessar la línia central d'un corredor perpendicular) i no només
quan topa amb un mur o sarc. A la detecció, amarra la caixa al centre
exacte del creuament per evitar drift sub-píxel i tria entre continuar
recta o girar a una perpendicular lliure. Mai mitja volta a un creuament
— la mitja volta queda reservada al cas de bloqueig sense alternatives.
2026-04-22 22:52:51 +02:00

99 lines
4.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <cstdint>
#include <optional>
#include <vector>
#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<Direction>& 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<Direction> perp;
std::vector<Direction> 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<float>;
// 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_;
};