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_`.
La mòmia ara decideix direcció a cada creuament de corredor (edge-detect
de travessar la línia central d'un corredor perpendicular) i no només
quan topa amb un mur o sarc. A la detecció, amarra la caixa al centre
exacte del creuament per evitar drift sub-píxel i tria entre continuar
recta o girar a una perpendicular lliure. Mai mitja volta a un creuament
— la mitja volta queda reservada al cas de bloqueig sense alternatives.