feat(room): mòmies surten emergint quan comença text-exit (fase 4)

Tres canvis coordinats que tanquen la cinemàtica desitjada de
ROOM_INTRO:

1. `spawn.duration_s` de mummy.yaml passa de 0.6 a 1.0 s. Les mòmies
   tarden ~1 segon a "emergir" — temps suficient perquè el jugador
   noti que estan apareixent, no que ja eren a la sala.

2. El disparador es mou de fase 5 (music_tail) a fase 4 (text-exit).
   Ara, just quan el text "PIRÀMIDE X, SALA Y" comença a sortir de
   pantalla, GameScene crida `entity_manager_.spawnPendingActors()`
   i les mòmies s'instancien ja en EMERGING. Visualment apareixen
   alhora que el text desapareix; durant la fase 5 acaben d'emergir
   i a l'entrada a PLAYING ja són ACTIVE — coincidint amb la caiguda
   aèria dels jugadors.

3. `completeIntro` afegeix un fallback: si arribem a PLAYING sense
   haver passat per fase 4 (config patològica, fases molt curtes),
   instanciem els pending igualment per garantir que la sala mai no
   queda sense enemics.

Renomenat: `hazards_spawn_triggered_` → `pending_actors_spawned_`,
nom que reflecteix què representa el flag al model nou.

El mètode privat `triggerHazardSpawnExit()` desapareix: era un
wrapper que ja només cridava `spawnPendingActors()`. Crida directa
des de `updateRoomTransition`, `respawnPlayersInstant` i
`completeIntro` (fallback).

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:40:28 +02:00
1 parent 6a6a1a52c7
commit 881e0ea1f6
3 files changed
+39 -39

No files matched your search

+26 -19
View File
@@ -623,7 +623,7 @@ void GameScene::enterRoomIntro() {
intro_completed_ = false;
intro_spawn_elapsed_ms_ = 0;
players_spawned_in_intro_.fill(false);
hazards_spawn_triggered_ = false;
pending_actors_spawned_ = false;
// Reset visual: arrenquem el FADE_IN d'INTRO i deixem el d'OUTRO
// inactiu (es re-armarà a `setState(ROOM_OUTRO)` i, dins d'aquell
@@ -677,24 +677,15 @@ void GameScene::respawnPlayersInstant() {
intro_completed_ = true;
transition_ = {};
// El salt debug a sala curt-circuit'a la cinemàtica d'INTRO i, amb
// ella, el trigger del SPAWN dels hazards dinàmics que hi viu a la
// fase 5 d'INTRO. Si no l'armàvem ací també, les mòmies de la sala
// entrada per editor quedarien congelades.
triggerHazardSpawnExit();
hazards_spawn_triggered_ = true;
// ella, l'spawn diferit dels DynamicHazards que viu a la fase 4
// d'INTRO. Si no els instanciàvem ací també, les mòmies de la sala
// entrada per editor no apareixerien mai.
entity_manager_.spawnPendingActors();
pending_actors_spawned_ = true;
Log::info("ROOM", "post-editor: spawn instantani, estat -> PLAYING");
}
#endif
void GameScene::triggerHazardSpawnExit() {
// Instancia els actors diferits (els que `loadFromLevel` ha empès a
// `pending_actors_`) i els arma el `triggerSpawnExit()` perquè entrin
// ja en EMERGING. L'aparició visual de la mòmia és aquesta crida.
const std::size_t PENDING = entity_manager_.pendingActorCount();
entity_manager_.spawnPendingActors();
Log::info("ROOM", "INTRO: spawn diferit de {} actors", PENDING);
}
void GameScene::triggerPlayerSpawn(std::size_t slot_index) {
if (slot_index >= players_.size()) { return; }
if (!players_active_[slot_index]) { return; }
@@ -844,6 +835,15 @@ void GameScene::completeIntro() {
if (intro_completed_) { return; }
intro_completed_ = true;
intro_spawn_elapsed_ms_ = 0;
// Fallback defensiu: si per qualsevol camí (config de fases amb tots
// els intervals molt curts, salt manual) hem arribat a PLAYING sense
// haver passat per la fase 4 que dispara l'spawn diferit, ho fem ara
// mateix. Sense això, les mòmies pendents quedarien per sempre a
// `pending_actors_` i la sala no tindria enemics.
if (!pending_actors_spawned_) {
entity_manager_.spawnPendingActors();
pending_actors_spawned_ = true;
}
// Spawn dels slots amb delay <= 0 (el cas habitual de P1). Els
// altres cauen frame a frame dins `updateRoomTransition` a mesura
// que `intro_spawn_elapsed_ms_` supera el seu delay. Així P1 cau
@@ -880,7 +880,7 @@ void GameScene::setState(RoomFsm::State new_state) {
intro_completed_ = false;
intro_spawn_elapsed_ms_ = 0;
players_spawned_in_intro_.fill(false);
hazards_spawn_triggered_ = false;
pending_actors_spawned_ = false;
}
auto* audio = Audio::get();
// PLAYING: arrenca la pista de joc en loop infinit. Netegem el
@@ -946,9 +946,16 @@ void GameScene::updateRoomTransition(int delta_ms) {
// mòmies surten visualment "del terra" just després que el text desapareix
// i abans que caiguin els jugadors — la sala arrenca amb tots els actors
// gairebé alhora (les mòmies amb un petit head-start `spawn.duration_s`).
if (state_ == RoomFsm::State::ROOM_INTRO && transition_.phase >= 5 && !hazards_spawn_triggered_) {
triggerHazardSpawnExit();
hazards_spawn_triggered_ = true;
// ROOM_INTRO, fase 4 = text-exit (el text està desapareixent). Instanciem
// els actors diferits (mòmies) — `EntityManager::spawnPendingActors` els
// crea ja en EMERGING. Visualment, "surten del terra" alhora que el text
// surt de pantalla. Amb spawn.duration_s ~ 1.0 s, queden ACTIVE
// coincidint amb la caiguda dels jugadors a music_tail.
if (state_ == RoomFsm::State::ROOM_INTRO && transition_.phase >= 4 && !pending_actors_spawned_) {
const std::size_t PENDING = entity_manager_.pendingActorCount();
entity_manager_.spawnPendingActors();
pending_actors_spawned_ = true;
Log::info("ROOM", "INTRO fase 4: spawn diferit de {} actors", PENDING);
}
// OUTRO: arrenquem `outro_fade_` la primera vegada que entrem a la