99 lines
4.1 KiB
C++
99 lines
4.1 KiB
C++
#pragma once
|
||
|
||
#include <cstdint>
|
||
#include <functional>
|
||
#include <string>
|
||
#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). S'inclouen els tiles adjacents per N/S/E/O i també les
|
||
// quatre cantonades diagonals (NO/NE/SO/SE), perquè el jugador les
|
||
// trepitja en doblar les cantonades del sarcòfag i, sense comptar-les,
|
||
// el cerco s'obriria abans d'haver-se tancat visualment. Els tiles 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;
|
||
void onRemoved() 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_;
|
||
|
||
// Clau (basename del fitxer) al Resource::Cache del so que es dispara
|
||
// a la transició CLOSED → OPENING. Buit ⇒ no sona res.
|
||
std::string opening_sound_key_;
|
||
// Variació uniforme del ratio de reproducció al voltant de 1.0. Cada
|
||
// obertura sorteja [1 - spread, 1 + spread]. 0 ⇒ ratio fix a 1.0.
|
||
float opening_pitch_spread_{0.0F};
|
||
|
||
bool opened_event_emitted_{false};
|
||
};
|