refactor(em): separa entitats en dos sacs (map_entities_/actors_)

EntityManager guanya dues llistes lògiques:
- `map_entities_` — decoració, static_hazards (Hole, FloorTrap, WallTrap)
  i interactables (Sarcophagus, Door). Persistents tota la sala.
- `actors_` — DynamicHazards (Mummy, RollingRock, futurs enemics).
  Tipats com `unique_ptr<DynamicHazard>`, sense dynamic_cast.

`add()` enruta automàticament segons sigui DynamicHazard o no.
`update`, `notifyPlayerTilePainted`, `findFloorTriggerAt`, `removeEntity`,
`reload` i `renderAll` iteren el(s) sac(s) que toca.

`checkPlayerCollisions`: el filtre de SPAWN sobre el sac d'actors ja no
necessita dynamic_cast — `actor->isSpawning()` és directe. El filtre per
floor decals només toca map_entities_ (els decals viuen exclusivament
allà). Filtres per-jugador extrets a helpers per acotar complexitat.

API nova: `getMapEntities()` i `getActors()` exposen cada sac per
separat. `getEntities()` es manté com a concatenació per retro-compat
(editor, z-sort debug).

`GameScene::triggerHazardSpawnExit` itera `getActors()` directe — fora
dynamic_cast.

`GameScene::render` mergeja els tres orígens (map + actors + players)
en un sol vector temporal abans del stable_sort per `feet_y` — mateix
patró que ja s'usava amb players, ara amb una transformació més.

Comportament idèntic: les momies segueixen instanciant-se al
loadFromLevel. La preparació per al spawn diferit ve en el commit
següent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
JailDesignerandClaude Opus 4.7 committed 2026-05-11 10:23:44 +02:00
1 parent 3b378e2b29
commit 62f4b857ef
4 files changed
+149 -70

No files matched your search

+15 -5
View File
@@ -688,9 +688,11 @@ void GameScene::respawnPlayersInstant() {
void GameScene::triggerHazardSpawnExit() {
int count = 0;
for (auto* entity : entity_manager_.getEntities()) {
if (auto* hazard = dynamic_cast<DynamicHazard*>(entity); hazard != nullptr && hazard->isSpawning()) {
hazard->triggerSpawnExit();
// `getActors()` retorna directament `DynamicHazard*` — sense
// dynamic_cast (els actors viuen en el seu sac dedicat).
for (auto* actor : entity_manager_.getActors()) {
if (actor->isSpawning()) {
actor->triggerSpawnExit();
++count;
}
}
@@ -1788,10 +1790,18 @@ void GameScene::render() {
};
std::vector<Renderable> order;
order.reserve(entity_manager_.entityCount() + players_.size());
const auto ENTITIES = entity_manager_.getEntities();
std::ranges::transform(ENTITIES, std::back_inserter(order), [](Entity* e) {
// Mergem els dos sacs (map + actors) i els jugadors en un sol
// vector temporal — el `stable_sort` per `feet_y` posterior fa el
// z-order interleaving entre els tres orígens sense importar el
// sac d'on venen.
const auto MAP = entity_manager_.getMapEntities();
std::ranges::transform(MAP, std::back_inserter(order), [](Entity* e) {
return Renderable{.feet_y = e->getFeetY(), .entity = e, .player = nullptr};
});
const auto ACTORS = entity_manager_.getActors();
std::ranges::transform(ACTORS, std::back_inserter(order), [](DynamicHazard* a) -> Renderable {
return Renderable{.feet_y = a->getFeetY(), .entity = a, .player = nullptr};
});
// Tots els jugadors entren al z-sort. Els fora de joc fan no-op a
// `render()`; els actius (inclosos AIRBORNE durant l'spawn aeri)
// es pinten amb el seu feet_y normal. La sombra que afecta la