87 lines
3.5 KiB
C++
87 lines
3.5 KiB
C++
#pragma once
|
||
|
||
#include <cstdint>
|
||
#include <functional>
|
||
#include <unordered_set>
|
||
|
||
#include "external/fkyaml_node.hpp"
|
||
#include "game/entities/animation_component.hpp"
|
||
#include "game/entities/interactable.hpp"
|
||
|
||
// Sarcòfag: rectangle de tiles que el jugador ha de rodar completament pel
|
||
// perímetre per "obrir". Tres estats (com la porta):
|
||
// CLOSED → inicial; `visited_` s'acumula amb les notificacions.
|
||
// OPENING → `visited_ == perimeter_tiles_`; reprodueix animació d'obertura.
|
||
// OPEN → emitit l'event `sarcophagus_opened`.
|
||
//
|
||
// El càlcul de `perimeter_tiles_` es fa al ctor a partir de la posició del
|
||
// sarcòfag a la graella del Map (`anchor_col`, `anchor_row`) i de les
|
||
// dimensions en tiles (`tile_size_w_`, `tile_size_h_`, llegits del YAML
|
||
// plantilla). Es recorren els tiles adjacents per N/S/E/O; els que
|
||
// cauen dins d'un altre sarcòfag es descarten (predicat opcional). Si no
|
||
// s'aporta el predicat, s'inclouen tots.
|
||
class Sarcophagus : public Interactable {
|
||
public:
|
||
enum class State : std::uint8_t { CLOSED,
|
||
OPENING,
|
||
OPEN };
|
||
|
||
// `is_blocked` rep un (col,row) i retorna true si el tile està ocupat
|
||
// per un altre sarcòfag o és fora del playfield (és a dir, NO és un
|
||
// tile de corredor vàlid). L'usa `EntityManager` passant-li
|
||
// `Map::collides` traduït a tiles. Si és nullptr, cap tile es descarta
|
||
// i el perímetre es computa purament geomètric (útil als tests).
|
||
using BlockedPredicate = std::function<bool(int col, int row)>;
|
||
|
||
Sarcophagus(const fkyaml::node& yaml, int anchor_col, int anchor_row, const BlockedPredicate& is_blocked);
|
||
explicit Sarcophagus(const fkyaml::node& yaml); // variant sense ancoratge (usat pel factory)
|
||
|
||
// Configura ancoratge i (re)calcula perímetre. Útil quan el factory
|
||
// crea l'instància abans de conèixer la posició; el `EntityLoader`
|
||
// invoca aquesta funció amb les coordenades del spawn.
|
||
void place(int anchor_col, int anchor_row, const BlockedPredicate& is_blocked);
|
||
|
||
void update(float delta_time) override;
|
||
void notifyTilePainted(int col, int row) override;
|
||
|
||
[[nodiscard]] auto getState() const -> State { return state_; }
|
||
[[nodiscard]] auto getPerimeterSize() const -> std::size_t { return perimeter_tiles_.size(); }
|
||
[[nodiscard]] auto getVisitedCount() const -> std::size_t { return visited_.size(); }
|
||
|
||
// Dimensions del rectangle en tiles (p.ex. 3×1 per al sarcòfag
|
||
// estàndard). Exposat per al smoke test i per al render futur.
|
||
[[nodiscard]] auto getTileWidth() const -> int { return tile_size_w_; }
|
||
[[nodiscard]] auto getTileHeight() const -> int { return tile_size_h_; }
|
||
|
||
private:
|
||
struct TileKey {
|
||
int col;
|
||
int row;
|
||
auto operator==(const TileKey& o) const -> bool { return col == o.col && row == o.row; }
|
||
};
|
||
struct TileKeyHash {
|
||
auto operator()(const TileKey& k) const -> std::size_t {
|
||
// Combinació simple; els valors són petits (<100) i el set no és gran.
|
||
return (static_cast<std::size_t>(k.col) * 131U) ^ static_cast<std::size_t>(k.row);
|
||
}
|
||
};
|
||
|
||
void computePerimeter(const BlockedPredicate& is_blocked);
|
||
|
||
int tile_size_w_{1};
|
||
int tile_size_h_{1};
|
||
int anchor_col_{0};
|
||
int anchor_row_{0};
|
||
|
||
std::unordered_set<TileKey, TileKeyHash> perimeter_tiles_;
|
||
std::unordered_set<TileKey, TileKeyHash> visited_;
|
||
|
||
State state_{State::CLOSED};
|
||
|
||
AnimationComponent anim_closed_;
|
||
AnimationComponent anim_opening_;
|
||
AnimationComponent anim_open_;
|
||
|
||
bool opened_event_emitted_{false};
|
||
};
|