From e3cf2eb7730f1d43bfc91ffe1a00541eb03bddb0 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 23 Apr 2026 13:22:02 +0200 Subject: [PATCH] =?UTF-8?q?refactor(entities):=20rename=20notifyTileEntere?= =?UTF-8?q?d=20=E2=86=92=20notifyTilePainted=20i=20escolta=20tile=5Fpainte?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/game/entities/concrete/sarcophagus.cpp | 2 +- source/game/entities/concrete/sarcophagus.hpp | 2 +- source/game/entities/entity.cpp | 2 +- source/game/entities/entity.hpp | 7 ++++--- source/game/entities/entity_manager.cpp | 15 +++++++------- source/game/entities/entity_manager.hpp | 13 ++++++------ tests/test_entities_smoke.cpp | 20 ++++++++++--------- 7 files changed, 33 insertions(+), 28 deletions(-) diff --git a/source/game/entities/concrete/sarcophagus.cpp b/source/game/entities/concrete/sarcophagus.cpp index bfdcdb6..44b4147 100644 --- a/source/game/entities/concrete/sarcophagus.cpp +++ b/source/game/entities/concrete/sarcophagus.cpp @@ -112,7 +112,7 @@ void Sarcophagus::update(float delta_time) { } } -void Sarcophagus::notifyTileEntered(int col, int row) { +void Sarcophagus::notifyTilePainted(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; } diff --git a/source/game/entities/concrete/sarcophagus.hpp b/source/game/entities/concrete/sarcophagus.hpp index 5cc5754..201701b 100644 --- a/source/game/entities/concrete/sarcophagus.hpp +++ b/source/game/entities/concrete/sarcophagus.hpp @@ -42,7 +42,7 @@ class Sarcophagus : public Interactable { void place(int anchor_col, int anchor_row, const BlockedPredicate& is_blocked); void update(float delta_time) override; - void notifyTileEntered(int col, int row) override; + void notifyTilePainted(int col, int row) override; [[nodiscard]] auto getState() const -> State { return state_; } [[nodiscard]] auto getPerimeterSize() const -> std::size_t { return perimeter_tiles_.size(); } diff --git a/source/game/entities/entity.cpp b/source/game/entities/entity.cpp index 0966ec8..fb0e3e9 100644 --- a/source/game/entities/entity.cpp +++ b/source/game/entities/entity.cpp @@ -23,7 +23,7 @@ void Entity::onPlayerContact(Player* /*player*/) { // No-op per defecte. } -void Entity::notifyTileEntered(int /*col*/, int /*row*/) { +void Entity::notifyTilePainted(int /*col*/, int /*row*/) { // No-op per defecte; només `Sarcophagus` l'usa. } diff --git a/source/game/entities/entity.hpp b/source/game/entities/entity.hpp index ce6922d..66ebf8f 100644 --- a/source/game/entities/entity.hpp +++ b/source/game/entities/entity.hpp @@ -77,9 +77,10 @@ class Entity { virtual void onPlayerContact(Player* player); // Hook que el `EntityManager` invoca per a cada entitat cada vegada - // que el jugador entra en un tile nou. Només `Sarcophagus` ho consumeix - // (per marcar perímetre visitat); per defecte no-op. - virtual void notifyTileEntered(int col, int row); + // que un tile es pinta (via PaintLayer, com a conseqüència d'un + // jugador trepitjant un sòl pintable). Només `Sarcophagus` ho consumeix + // (per marcar perímetre); per defecte no-op. + virtual void notifyTilePainted(int col, int row); [[nodiscard]] auto getName() const -> const std::string& { return name_; } [[nodiscard]] auto getTilePos() const -> TilePos { return tile_; } diff --git a/source/game/entities/entity_manager.cpp b/source/game/entities/entity_manager.cpp index 2b62629..dbfeed7 100644 --- a/source/game/entities/entity_manager.cpp +++ b/source/game/entities/entity_manager.cpp @@ -24,12 +24,13 @@ EntityManager::EntityManager() { } }); - // Subscripció al moviment del jugador: quan publica `on_tile_entered` - // amb payload `TilePos`, fem fan-out a totes les entitats perquè els - // sarcòfags puguin actualitzar el seu `visited_`. - tile_sub_id_ = bus_.subscribe("on_tile_entered", [this](const Event& ev) { + // 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) { notifyPlayerEnteredTile(tile->col, tile->row); } + if (tile != nullptr) { notifyPlayerTilePainted(tile->col, tile->row); } }); } @@ -66,8 +67,8 @@ void EntityManager::update(float delta_time) { for (auto& e : entities_) { e->update(delta_time); } } -void EntityManager::notifyPlayerEnteredTile(int col, int row) { - for (auto& e : entities_) { e->notifyTileEntered(col, row); } +void EntityManager::notifyPlayerTilePainted(int col, int row) { + for (auto& e : entities_) { e->notifyTilePainted(col, row); } } auto EntityManager::getEntities() const -> std::vector { diff --git a/source/game/entities/entity_manager.hpp b/source/game/entities/entity_manager.hpp index 2cb9559..000d6fc 100644 --- a/source/game/entities/entity_manager.hpp +++ b/source/game/entities/entity_manager.hpp @@ -17,9 +17,9 @@ class Player; // 2. `update(dt)` propaga el tick a totes. // 3. Comptador de sarcòfags oberts — quan iguala el total registrat, // crida `door_->open()` automàticament. -// 4. `notifyPlayerEnteredTile(col, row)` — fan-out del hook per a tots +// 4. `notifyPlayerTilePainted(col, row)` — fan-out del hook per a tots // els sarcòfags (i qualsevol entitat futura que s'hi subscrigui via -// `notifyTileEntered`). +// `notifyTilePainted`). // // NO és un singleton: es crea una instància per nivell. De moment no // s'usa encara al GameScene (andamiatge pur). @@ -74,10 +74,11 @@ class EntityManager { // reflecteixi l'estat del frame actual. void checkPlayerCollisions(const std::vector& players); - // Fan-out del hook de moviment de jugador: per a cada sarcòfag, - // cridar notifyTileEntered. Les entitats no-sarcòfag ho ignoren per - // defecte (veure Entity::notifyTileEntered). - void notifyPlayerEnteredTile(int col, int row); + // Fan-out del hook de pintat: per a cada sarcòfag, cridar + // notifyTilePainted. Les entitats no-sarcòfag ho ignoren per defecte + // (veure Entity::notifyTilePainted). Invocat com a reacció a + // `tile_painted` al bus (re-publicat pel PaintLayer un cop filtrat). + void notifyPlayerTilePainted(int col, int row); // Carrega els spawns d'un YAML de nivell i els afegeix al manager: // 1. Llegeix el bloc `entities:` via EntityLoader. diff --git a/tests/test_entities_smoke.cpp b/tests/test_entities_smoke.cpp index f82bbd7..8541ea5 100644 --- a/tests/test_entities_smoke.cpp +++ b/tests/test_entities_smoke.cpp @@ -4,7 +4,7 @@ // T1 factory — tots els tipus es registren i es creen. // T2 loader — llegeix `data/maps/test_entities.yaml` i retorna els spawns. // T3 event bus — subscribe + publish + payload (entity*, int). -// T4 sarcòfag — perímetre computat correctament; notifyTileEntered +// T4 sarcòfag — perímetre computat correctament; notifyTilePainted // passa a OPENING quan es visiten tots els tiles. // T5 manager — 2 sarcs + 1 door: forçant l'obertura dels sarcs, la porta // s'obri sola; contactar la porta OPEN emet `level_complete`. @@ -138,14 +138,14 @@ namespace { REQUIRE(sarc->getState() == Sarcophagus::State::CLOSED); // Visitem un tile que NO és al perímetre: res canvia. - sarc->notifyTileEntered(5, 5); + sarc->notifyTilePainted(5, 5); REQUIRE(sarc->getVisitedCount() == 0); // Visitem tots els del perímetre. const std::vector> PERIM = { {1, 0}, {2, 0}, {3, 0}, {1, 2}, {2, 2}, {3, 2}, {0, 1}, {4, 1}, }; - for (const auto& [c, r] : PERIM) { sarc->notifyTileEntered(c, r); } + for (const auto& [c, r] : PERIM) { sarc->notifyTilePainted(c, r); } REQUIRE(sarc->getVisitedCount() == 8); REQUIRE(sarc->getState() == Sarcophagus::State::OPENING); @@ -186,12 +186,12 @@ namespace { const int W = s->getTileWidth(); const int H = s->getTileHeight(); for (int dc = 0; dc < W; ++dc) { - s->notifyTileEntered(COL0 + dc, ROW0 - 1); - s->notifyTileEntered(COL0 + dc, ROW0 + H); + s->notifyTilePainted(COL0 + dc, ROW0 - 1); + s->notifyTilePainted(COL0 + dc, ROW0 + H); } for (int dr = 0; dr < H; ++dr) { - s->notifyTileEntered(COL0 - 1, ROW0 + dr); - s->notifyTileEntered(COL0 + W, ROW0 + dr); + s->notifyTilePainted(COL0 - 1, ROW0 + dr); + s->notifyTilePainted(COL0 + W, ROW0 + dr); } }; forceOpen(s1); @@ -310,7 +310,9 @@ namespace { REQUIRE(ANCHOR_ROW == 4); // 4) Fan-out del bus: un sarcòfag ancorat a (3,4) mida 3×1 rep via - // on_tile_entered una visita sobre el seu perímetre → state canvia. + // tile_painted una notificació sobre cada tile del seu perímetre + // → state canvia. Publiquem directament `tile_painted` (sense + // passar pel PaintLayer) per aïllar el fan-out de l'EntityManager. EntityManager manager; auto sarc = std::make_unique(EntityLoader::loadTemplate("sarcophagus")); sarc->place(3, 4, {}); @@ -323,7 +325,7 @@ namespace { {2, 4}, {6, 4}, }; for (const auto& [c, r] : PERIM) { - manager.getBus().publish("on_tile_entered", TilePos{.col = c, .row = r}); + manager.getBus().publish("tile_painted", TilePos{.col = c, .row = r}); } REQUIRE(sarc_raw->getState() == Sarcophagus::State::OPENING); }