feat(entities): andamiatge del sistema d'entitats (classes, factory, bus, YAML, placeholders, smoke tests)
This commit is contained in:
1 parent
984cc820c0
commit
96b71b0f65
48 files changed
+1957
No files matched your search
@@ -0,0 +1,127 @@
|
||||
#include "game/entities/concrete/sarcophagus.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "game/entities/entity_factory.hpp"
|
||||
#include "game/entities/event_bus.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
auto buildAnimation(const fkyaml::node& yaml, const std::string& key) -> AnimationComponent {
|
||||
std::vector<int> frames = {0};
|
||||
float fps = 1.0F;
|
||||
bool loop = true;
|
||||
if (yaml.contains("animations") && yaml["animations"].contains(key)) {
|
||||
const auto& anim = yaml["animations"][key];
|
||||
if (anim.contains("frames")) {
|
||||
frames.clear();
|
||||
for (const auto& f : anim["frames"]) {
|
||||
// cppcheck-suppress useStlAlgorithm
|
||||
frames.push_back(f.get_value<int>());
|
||||
}
|
||||
if (frames.empty()) { frames.push_back(0); }
|
||||
}
|
||||
if (anim.contains("fps")) { fps = anim["fps"].get_value<float>(); }
|
||||
if (anim.contains("loop")) { loop = anim["loop"].get_value<bool>(); }
|
||||
}
|
||||
return {frames, fps, loop};
|
||||
}
|
||||
|
||||
// Llig `tile_size: [w, h]` del YAML. Fallback [1,1] si no existeix.
|
||||
void readTileSize(const fkyaml::node& yaml, int& w, int& h) {
|
||||
w = 1;
|
||||
h = 1;
|
||||
if (!yaml.contains("tile_size")) { return; }
|
||||
const auto& ts = yaml["tile_size"];
|
||||
if (ts.is_sequence() && ts.size() == 2) {
|
||||
w = ts[0].get_value<int>();
|
||||
h = ts[1].get_value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Sarcophagus::Sarcophagus(const fkyaml::node& yaml, int anchor_col, int anchor_row, const BlockedPredicate& is_blocked)
|
||||
: Sarcophagus(yaml) {
|
||||
place(anchor_col, anchor_row, is_blocked);
|
||||
}
|
||||
|
||||
Sarcophagus::Sarcophagus(const fkyaml::node& yaml)
|
||||
: anim_closed_(buildAnimation(yaml, "closed")),
|
||||
anim_opening_(buildAnimation(yaml, "opening")),
|
||||
anim_open_(buildAnimation(yaml, "open")) {
|
||||
name_ = yaml.contains("name") ? yaml["name"].get_value<std::string>() : std::string("sarcophagus");
|
||||
collides_ = true;
|
||||
interactable_ = true;
|
||||
readTileSize(yaml, tile_size_w_, tile_size_h_);
|
||||
anim_ = anim_closed_;
|
||||
}
|
||||
|
||||
void Sarcophagus::place(int anchor_col, int anchor_row, const BlockedPredicate& is_blocked) {
|
||||
anchor_col_ = anchor_col;
|
||||
anchor_row_ = anchor_row;
|
||||
tile_.col = anchor_col;
|
||||
tile_.row = anchor_row;
|
||||
computePerimeter(is_blocked);
|
||||
visited_.clear();
|
||||
}
|
||||
|
||||
void Sarcophagus::computePerimeter(const BlockedPredicate& is_blocked) {
|
||||
perimeter_tiles_.clear();
|
||||
|
||||
// El perímetre ortogonal d'un rectangle (w,h) de tiles són les files
|
||||
// de tiles immediatament N, S, E, O. No s'hi inclouen les cantonades
|
||||
// diagonals (tile NO, NE, SO, SE), perquè el jugador fa passes pels
|
||||
// quatre costats i no pot "tallar" per una diagonal: això garanteix
|
||||
// que el recorregut completa només si passa per cada costat.
|
||||
//
|
||||
// - Fila Nord: (col, row-1) per col ∈ [col0, col0+w-1]
|
||||
// - Fila Sud: (col, row+h) per col ∈ [col0, col0+w-1]
|
||||
// - Columna O: (col0-1, row) per row ∈ [row0, row0+h-1]
|
||||
// - Columna E: (col0+w, row) per row ∈ [row0, row0+h-1]
|
||||
|
||||
auto maybe_insert = [&](int col, int row) {
|
||||
if (is_blocked && is_blocked(col, row)) { return; }
|
||||
perimeter_tiles_.insert({col, row});
|
||||
};
|
||||
|
||||
// Nord i Sud
|
||||
for (int dc = 0; dc < tile_size_w_; ++dc) {
|
||||
maybe_insert(anchor_col_ + dc, anchor_row_ - 1);
|
||||
maybe_insert(anchor_col_ + dc, anchor_row_ + tile_size_h_);
|
||||
}
|
||||
// Oest i Est
|
||||
for (int dr = 0; dr < tile_size_h_; ++dr) {
|
||||
maybe_insert(anchor_col_ - 1, anchor_row_ + dr);
|
||||
maybe_insert(anchor_col_ + tile_size_w_, anchor_row_ + dr);
|
||||
}
|
||||
}
|
||||
|
||||
void Sarcophagus::update(float delta_time) {
|
||||
Entity::update(delta_time);
|
||||
|
||||
if (state_ == State::OPENING && anim_.has_value() && anim_->completed()) {
|
||||
state_ = State::OPEN;
|
||||
anim_ = anim_open_;
|
||||
if (anim_.has_value()) { anim_->reset(); }
|
||||
if (!opened_event_emitted_ && bus_ != nullptr) {
|
||||
bus_->publish("sarcophagus_opened", static_cast<Entity*>(this));
|
||||
opened_event_emitted_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sarcophagus::notifyTileEntered(int col, int row) {
|
||||
if (state_ != State::CLOSED) { return; }
|
||||
const TileKey KEY{.col = col, .row = row};
|
||||
if (perimeter_tiles_.find(KEY) == perimeter_tiles_.end()) { return; }
|
||||
visited_.insert(KEY);
|
||||
if (visited_.size() >= perimeter_tiles_.size()) {
|
||||
state_ = State::OPENING;
|
||||
anim_ = anim_opening_;
|
||||
if (anim_.has_value()) { anim_->reset(); }
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_ENTITY("sarcophagus", Sarcophagus)
|
||||
Reference in new issue
Block a user