refactor(entities): polimorfisme per substituir dynamic_casts
Quatre virtuals nous a `Entity` capten decisions que abans vivien al manager com a discriminacions de tipus: - `onWhipHit(WhipAttack*)` → bool. Default no-op/false. Override a `Mummy` per entrar HURT si és whippable. WhipAttack::applyDamage passa de `dynamic_cast<Mummy*>` + `isWhippable()` a una crida polimòrfica neta. RollingRock no override → immune al látigo per disseny, sense flag. - `onJustSpawned()`. Default no-op. Override a `DynamicHazard` per disparar `triggerSpawnExit()`. `spawnPendingActors` i `spawnActor` passen de `dynamic_cast<DynamicHazard*>(observer)->triggerSpawnExit()` a `observer->onJustSpawned()`. - `onRemoved()`. Default no-op. Override a `Sarcophagus` per cridar `entity_manager_->onSarcophagusRemoved()`. `removeEntity` passa de `getName() == "sarcophagus"` a dispatch polimòrfic. - `shouldReceiveContact()`. Default `collides() || triggersOnOverlap()`. Override a `DynamicHazard` per excloure SPAWN. `checkPlayerCollisions` passa de tres condicions inline per a sacs distints a una crida unificada. `whipDirToMummyDir` es mou de whip_attack.cpp a mummy.cpp — el mapping viu on s'usa (dins de `Mummy::onWhipHit`). `hitMummyIfApplicable` es renomena a `hitEntityIfApplicable`: ja no discrimina tipus, aplica `entity->onWhipHit` i marca `already_hit_` segons el retorn. Comportament idèntic, tests verds. Aquest commit prepara el següent: amb el polimorfisme madurat, el sac dual ja no aporta valor i podem tornar a un únic `entities_`.
This commit is contained in:
1 parent
e8418f89e0
commit
28ff425c89
11 files changed
+177
-58
No files matched your search
@@ -100,20 +100,17 @@ void EntityManager::checkPlayerCollisions(const std::vector<Player*>& players) {
|
||||
}
|
||||
};
|
||||
|
||||
// Sac de mapa: filtre per col·lisió / trigger. Tanquem el gap latent
|
||||
// de FloorTrap/WallTrap (que tenien `collides_=false` i mai no es
|
||||
// disparaven) i obrim la porta a hazards no-físics com `Hole`.
|
||||
// Tots dos sacs apliquen el mateix filtre polimòrfic
|
||||
// `shouldReceiveContact()`. Per defecte equival a `collides() ||
|
||||
// triggersOnOverlap()`; `DynamicHazard` ho afina afegint el filtre de
|
||||
// SPAWN (mentre està emergint torna false). Sense dynamic_cast,
|
||||
// sense filtres específics al callsite.
|
||||
for (const auto& entity : map_entities_) {
|
||||
if (!entity->collides() && !entity->triggersOnOverlap()) { continue; }
|
||||
if (!entity->shouldReceiveContact()) { continue; }
|
||||
apply_contacts(entity.get());
|
||||
}
|
||||
// Sac d'actors (DynamicHazards): tots fan dany quan són actius.
|
||||
// Saltem els que estan en SPAWN: visibles però sense interacció
|
||||
// durant la fase inicial de ROOM_INTRO i durant futurs spawns en
|
||||
// runtime. Sense dynamic_cast: el tipus ja és DynamicHazard.
|
||||
for (const auto& actor : actors_) {
|
||||
if (actor->isSpawning()) { continue; }
|
||||
if (!actor->collides() && !actor->triggersOnOverlap()) { continue; }
|
||||
if (!actor->shouldReceiveContact()) { continue; }
|
||||
apply_contacts(actor.get());
|
||||
}
|
||||
}
|
||||
@@ -500,17 +497,13 @@ void EntityManager::loadFromLevel(const std::string& level_yaml_path) {
|
||||
|
||||
void EntityManager::spawnPendingActors() {
|
||||
// Itera els pending i els instancia via `addSpawn`. El dispatch d'`add()`
|
||||
// els enruta a `actors_`. El `triggerSpawnExit()` posterior arma el
|
||||
// comptador de SPAWN — la mòmia neix ja en EMERGING, sense esperes.
|
||||
// els enruta a `actors_`. `onJustSpawned()` és polimòrfic: la base
|
||||
// no fa res, DynamicHazard l'override per disparar `triggerSpawnExit()`
|
||||
// i entrar a EMERGING. Sense dynamic_cast: la decisió viu dins de
|
||||
// cada entitat.
|
||||
for (const auto& pa : pending_actors_) {
|
||||
Entity* observer = addSpawn(pa.spawn_node);
|
||||
if (observer == nullptr) { continue; }
|
||||
// Per categoria YAML (`type: dynamic_hazard`) sabem que és
|
||||
// DynamicHazard — el dynamic_cast aquí val zero i evita
|
||||
// assumptions implícites sobre la jerarquia.
|
||||
if (auto* dh = dynamic_cast<DynamicHazard*>(observer); dh != nullptr) {
|
||||
dh->triggerSpawnExit();
|
||||
}
|
||||
if (observer != nullptr) { observer->onJustSpawned(); }
|
||||
}
|
||||
pending_actors_.clear();
|
||||
}
|
||||
@@ -530,7 +523,9 @@ auto EntityManager::spawnActor(const std::string& type, int col, int row) -> Dyn
|
||||
}
|
||||
Entity* observer = addSpawn(spawn);
|
||||
if (observer == nullptr) { return nullptr; }
|
||||
auto* dh = dynamic_cast<DynamicHazard*>(observer);
|
||||
if (dh != nullptr) { dh->triggerSpawnExit(); }
|
||||
return dh;
|
||||
// `onJustSpawned` polimòrfic: per a DynamicHazards arma EMERGING; per
|
||||
// a un type erroni (no dynamic_hazard) queda no-op i retornem nullptr
|
||||
// mitjançant el cast final, deixant la signatura `DynamicHazard*` neta.
|
||||
observer->onJustSpawned();
|
||||
return dynamic_cast<DynamicHazard*>(observer);
|
||||
}
|
||||
Reference in new issue
Block a user