diff --git a/data/entities/mummy/mummy.yaml b/data/entities/mummy/mummy.yaml index 61fba40..3c42890 100644 --- a/data/entities/mummy/mummy.yaml +++ b/data/entities/mummy/mummy.yaml @@ -41,13 +41,13 @@ ai: direction_choice: random initial_direction: east -# Estat SPAWN: la mòmia naix visible i quieta al pixel_rect_ inicial, -# sense col·lisió. El GameScene li arma el comptador al final del -# text-exit de ROOM_INTRO; passats `duration_s` segons des d'aleshores, -# transita a ROAMING i empieza a deambular. Sense aquest bloc, default -# 0 ⇒ al primer update post-trigger passa directament a ACTIVE. +# Estat SPAWN: la mòmia s'instancia visible al pixel_rect_ inicial just +# quan GameScene crida `spawnPendingActors()` (a la subfase text-exit +# de ROOM_INTRO, fase 4). El `triggerSpawnExit` es dispara llavors +# mateix; passats `duration_s` segons transita a ROAMING i empieza a +# deambular. Sense aquest bloc, default 0 ⇒ ACTIVE immediat. spawn: - duration_s: 0.6 + duration_s: 1.0 stats: speed: 30 damage: 1 diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index f509f13..1ad5b06 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -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 diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index 15de616..2abd872 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -118,17 +118,6 @@ class GameScene : public Scene { // el futur. void triggerPlayerSpawn(std::size_t slot_index); - // Allibera tots els DynamicHazards de la sala (típicament mòmies) - // del seu estat SPAWN inicial: arma el comptador via - // `DynamicHazard::triggerSpawnExit()`. Es crida a `updateRoomTransition` - // quan ROOM_INTRO entra a la fase 5 (post text-exit), perquè les mòmies - // surten visualment "del terra" just després que el text desapareix - // i abans que caiguin els jugadors. Idempotent dins el cicle - // (`hazards_spawn_triggered_`); reset a `enterRoomIntro`. També - // cridada des de `respawnPlayersInstant` perquè el salt debug - // a sala no deixi les mòmies congelades. - void triggerHazardSpawnExit(); - // Cos del callback de fi de música d'INTRO. Idempotent: si ja s'ha // disparat aquest cicle (`intro_completed_`), no fa res. Marca el // flag, reseteja el timer post-callback i intenta spawn dels slots @@ -213,9 +202,13 @@ class GameScene : public Scene { // i a `setState(ROOM_INTRO)` (camí defensiu no canònic). std::array players_spawned_in_intro_{false, false}; - // Idempotència de `triggerHazardSpawnExit` dins d'un cicle d'INTRO. - // Reset a `enterRoomIntro` i a `setState(ROOM_INTRO)`. - bool hazards_spawn_triggered_{false}; + // Idempotència de la crida a `entity_manager_.spawnPendingActors()` + // dins d'un cicle d'INTRO. Es dispara una sola vegada quan + // ROOM_INTRO entra a la subfase text-exit (fase 4) i, defensivament, + // a `respawnPlayersInstant` i al fallback de `completeIntro` per a + // camins no canònics. Reset a `enterRoomIntro` i a + // `setState(ROOM_INTRO)`. + bool pending_actors_spawned_{false}; // Vista plana de punters crus — la fa servir Player com a llista // d'obstacles mòbils a la callback de col·lisió. Es construeix un sol