94 lines
3.6 KiB
C++
94 lines
3.6 KiB
C++
#include "game/entities/entity_manager.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <utility>
|
|
|
|
#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<TilePos>(&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) -> 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<Sarcophagus> 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) {
|
|
// ATENCIÓ: si en el futur una entitat publica al bus events que
|
|
// resulten en `entity_manager_.add()` (push_back a `entities_`), açò
|
|
// invalidaria l'iterador de la ranged-for. Avui no passa: els spawns
|
|
// runtime venen de fora del cicle d'`update` (p.ex. el polvet del
|
|
// saltador es spawneja durant `Player::tick`, abans d'aquest mètode).
|
|
for (auto& e : entities_) { e->update(delta_time); }
|
|
// Purga d'entitats marcades mortes aquest frame (efectes acabats,
|
|
// p.ex. polvet d'aterratge). Les persistents (sarcs, momies, etc.)
|
|
// mai no es marquen.
|
|
std::erase_if(entities_, [](const auto& e) { return e->isDead(); });
|
|
}
|
|
|
|
void EntityManager::notifyPlayerTilePainted(int col, int row) {
|
|
for (auto& e : entities_) { e->notifyTilePainted(col, row); }
|
|
}
|
|
|
|
auto EntityManager::getEntities() const -> std::vector<Entity*> {
|
|
std::vector<Entity*> 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.
|