Files
aee-arcade/source/game/entities/concrete/sarcophagus.hpp
T

96 lines
3.9 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 <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). 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;
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};
};