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
@@ -262,19 +262,10 @@ void WhipAttack::update(float delta_time) {
|
||||
|
||||
namespace {
|
||||
|
||||
auto whipDirToMummyDir(WhipAttack::Direction d) -> Mummy::Direction {
|
||||
switch (d) {
|
||||
case WhipAttack::Direction::NORTH:
|
||||
return Mummy::Direction::NORTH;
|
||||
case WhipAttack::Direction::SOUTH:
|
||||
return Mummy::Direction::SOUTH;
|
||||
case WhipAttack::Direction::EAST:
|
||||
return Mummy::Direction::EAST;
|
||||
case WhipAttack::Direction::WEST:
|
||||
return Mummy::Direction::WEST;
|
||||
}
|
||||
return Mummy::Direction::NONE;
|
||||
}
|
||||
// (El mapping WhipAttack::Direction → Mummy::Direction viu ara a
|
||||
// mummy.cpp, on s'usa des de `Mummy::onWhipHit`. Aquí només queda el
|
||||
// mapping a Player::Cardinal — usat per al friendly-fire entre
|
||||
// jugadors, que no és polimòrfic via Entity.)
|
||||
|
||||
auto whipDirToPlayerCardinal(WhipAttack::Direction d) -> Player::Cardinal {
|
||||
switch (d) {
|
||||
@@ -296,25 +287,24 @@ namespace {
|
||||
return STATE != Player::State::HURT && STATE != Player::State::EXITED && STATE != Player::State::DISABLED;
|
||||
}
|
||||
|
||||
// Tracta una mòmia candidata: si és whippable i la seua caja solapa
|
||||
// amb `damage_rect`, la posa a HURT i la marca al set. Extret per
|
||||
// acotar la complexitat cognitiva d'`applyDamage` (clang-tidy llindar).
|
||||
// Rep un `DynamicHazard*` (l'iterador d'`applyDamage` ja filtra el
|
||||
// sac d'actors); només cal el dynamic_cast a Mummy per descartar
|
||||
// altres hermanes (RollingRock i futurs DynamicHazards no whippables).
|
||||
void hitMummyIfApplicable(
|
||||
DynamicHazard* actor,
|
||||
// Tracta una entitat candidata: si la seva caixa solapa amb
|
||||
// `damage_rect`, delega la decisió a `entity->onWhipHit(attack)`.
|
||||
// Cada entitat decideix què fer (Mummy entra HURT, Torch retorna
|
||||
// false sense efecte, RollingRock no override → no-op). El retorn
|
||||
// true marca el set `already_hit` per evitar segons impactes dins del
|
||||
// mateix swing. Polimorfisme net — sense dynamic_cast.
|
||||
void hitEntityIfApplicable(
|
||||
Entity* entity,
|
||||
const SDL_FRect& damage_rect,
|
||||
WhipAttack::Direction dir,
|
||||
WhipAttack* attack,
|
||||
std::unordered_set<void*>& already_hit) {
|
||||
if (actor == nullptr || actor->isDead()) { return; }
|
||||
if (already_hit.contains(static_cast<void*>(actor))) { return; }
|
||||
auto* mummy = dynamic_cast<Mummy*>(actor);
|
||||
if (mummy == nullptr || !mummy->isWhippable()) { return; }
|
||||
const auto BOX = mummy->getCollisionRect();
|
||||
if (entity == nullptr || entity->isDead()) { return; }
|
||||
if (already_hit.contains(static_cast<void*>(entity))) { return; }
|
||||
const auto BOX = entity->getCollisionRect();
|
||||
if (!SDL_HasRectIntersectionFloat(&damage_rect, &BOX)) { return; }
|
||||
mummy->enterHurt(whipDirToMummyDir(dir));
|
||||
already_hit.insert(static_cast<void*>(actor));
|
||||
if (entity->onWhipHit(attack)) {
|
||||
already_hit.insert(static_cast<void*>(entity));
|
||||
}
|
||||
}
|
||||
|
||||
void hitPlayerIfApplicable(
|
||||
@@ -339,12 +329,14 @@ namespace {
|
||||
|
||||
void WhipAttack::applyDamage() {
|
||||
if (entity_manager_ != nullptr) {
|
||||
// Només els actors poden rebre el fuetejo (decoracions, sarcs,
|
||||
// forats... no són hits del whip). Iterem el sac `actors_`
|
||||
// directament — sense recórrer tot el mapa per descartar-ne la
|
||||
// major part.
|
||||
// Iterem el sac d'actors (DynamicHazards). Les entitats
|
||||
// estàtiques (sarc, antorxa, forat, traps) no reben fueteig per
|
||||
// disseny — la decisió viu en el flag de saco, no en filtres
|
||||
// dispersos. Si en el futur cal que un sarc reaccioni al fueteig
|
||||
// (p.ex. trencar-se), basta amb afegir un override de
|
||||
// `onWhipHit` allà i iterar també getMapEntities().
|
||||
for (auto* actor : entity_manager_->getActors()) {
|
||||
hitMummyIfApplicable(actor, damage_rect_, direction_, already_hit_);
|
||||
hitEntityIfApplicable(actor, damage_rect_, this, already_hit_);
|
||||
}
|
||||
}
|
||||
if (players_ != nullptr) {
|
||||
|
||||
Reference in new issue
Block a user