#include "game/entities/entity_manager.hpp" #include #include #include #include "game/entities/concrete/door.hpp" #include "game/entities/concrete/sarcophagus.hpp" #include "game/entities/entity.hpp" // NOTA: `loadFromLevel` viu a `entity_manager_loadlevel.cpp` per evitar // arrossegar `Resource::Cache`, `Surface` i les seues dependències (GIF, // àudio) a consumidors que només volen la base del manager — en // particular, el smoke test stand-alone. EntityManager::EntityManager() { // Subscripció al bus: cada sarcòfag obert incrementa el comptador; // quan iguala el total registrat, obrim la porta. La subscripció // sobreviu tot el nivell. sarc_sub_id_ = bus_.subscribe("sarcophagus_opened", [this](const Event& /*ev*/) { ++opened_sarcs_; if (total_sarcs_ > 0 && opened_sarcs_ >= total_sarcs_ && door_ != nullptr) { door_->open(); } }); // Subscripció al pintat: el PaintLayer publica `tile_painted` amb // payload `TilePos` un cop ha filtrat pintabilitat i aplicat la regla // "primer que pinta guanya". Fem fan-out a totes les entitats perquè // els sarcòfags puguin actualitzar el seu `visited_`. tile_sub_id_ = bus_.subscribe("tile_painted", [this](const Event& ev) { const auto* tile = std::get_if(&ev.payload); if (tile != nullptr) { notifyPlayerTilePainted(tile->col, tile->row); } }); } EntityManager::~EntityManager() { bus_.unsubscribe(sarc_sub_id_); bus_.unsubscribe(tile_sub_id_); } auto EntityManager::add(std::unique_ptr entity) -> Entity* { if (entity == nullptr) { return nullptr; } entity->setEventBus(&bus_); Entity* raw = entity.get(); entities_.push_back(std::move(entity)); // cppcheck-suppress returnDanglingLifetime return raw; } void EntityManager::setDoor(Door* door) { door_ = door; if (door_ != nullptr) { door_->setEventBus(&bus_); } } auto EntityManager::addSarcophagus(std::unique_ptr sarc) -> Sarcophagus* { if (sarc == nullptr) { return nullptr; } sarc->setEventBus(&bus_); Sarcophagus* raw = sarc.get(); entities_.push_back(std::move(sarc)); ++total_sarcs_; // cppcheck-suppress returnDanglingLifetime return raw; } void EntityManager::update(float delta_time) { for (auto& e : entities_) { e->update(delta_time); } } void EntityManager::notifyPlayerTilePainted(int col, int row) { for (auto& e : entities_) { e->notifyTilePainted(col, row); } } auto EntityManager::getEntities() const -> std::vector { std::vector out; out.reserve(entities_.size()); std::ranges::transform(entities_, std::back_inserter(out), [](const auto& e) { return e.get(); }); return out; } // NOTA: `renderAll` es defineix a `entity_manager_loadlevel.cpp`. Conté // blits via `Surface::render`, que dragaria el pipeline complet de render // (GIF, Canvas, Screen, àudio...) al smoke test. Separar-lo manté el smoke // autocontingut.