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,85 @@
|
||||
#include "game/entities/concrete/door.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "game/entities/entity_factory.hpp"
|
||||
#include "game/entities/event_bus.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
// Carrega una animació d'un bloc concret (closed/opening/open) amb
|
||||
// fallbacks tolerants. Si la clau no existeix, retorna una animació
|
||||
// d'un sol frame loopant: no fa estat invàlid, simplement no hi ha res
|
||||
// per reproduir.
|
||||
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};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Door::Door(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("door");
|
||||
collides_ = true;
|
||||
interactable_ = true;
|
||||
anim_ = anim_closed_;
|
||||
}
|
||||
|
||||
void Door::setAnimation(const AnimationComponent& anim) {
|
||||
anim_ = anim;
|
||||
if (anim_.has_value()) { anim_->reset(); }
|
||||
}
|
||||
|
||||
void Door::update(float delta_time) {
|
||||
Entity::update(delta_time);
|
||||
|
||||
// Transició OPENING → OPEN: amb l'animació `opening` configurada com a
|
||||
// loop=false, `completed()` passa a true quan s'ha recorregut l'últim
|
||||
// frame. Per a animacions d'un sol frame, el component la considera
|
||||
// completada immediatament (1 frame = 0 passos), així que la transició
|
||||
// ocorre al pròxim tick.
|
||||
if (state_ == State::OPENING && anim_.has_value() && anim_->completed()) {
|
||||
state_ = State::OPEN;
|
||||
setAnimation(anim_open_);
|
||||
}
|
||||
}
|
||||
|
||||
void Door::onPlayerContact(Player* /*player*/) {
|
||||
if (state_ != State::OPEN || level_complete_emitted_) { return; }
|
||||
if (bus_ != nullptr) { bus_->publish("level_complete"); }
|
||||
level_complete_emitted_ = true;
|
||||
}
|
||||
|
||||
void Door::open() {
|
||||
if (state_ != State::CLOSED) { return; }
|
||||
state_ = State::OPENING;
|
||||
setAnimation(anim_opening_);
|
||||
// Edge case: si `opening` té ≤1 frame i és loop=true, mai es
|
||||
// completarà. Per força, forcem loop=false del frame únic aquí. Els
|
||||
// placeholders defineixen `loop: false` explícitament al YAML, així
|
||||
// que aquest fallback només actua si algú dissenya malament.
|
||||
if (anim_.has_value() && anim_->frameCount() <= 1 && anim_->loops()) {
|
||||
anim_ = AnimationComponent({0}, 6.0F, false);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_ENTITY("door", Door)
|
||||
Reference in new issue
Block a user