From 3b378e2b292b63f51dfac61af30932a137dffaaf Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Mon, 11 May 2026 08:56:14 +0200 Subject: [PATCH] feat(room): allibera DynamicHazards del SPAWN al final del text-INTRO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GameScene ara crida `triggerHazardSpawnExit()` quan ROOM_INTRO entra a la fase 5 (music_tail, immediatament després del text-exit): itera els DynamicHazards de la sala i els arma el comptador `spawn.duration_s`. Així la cinemàtica queda: fade-in → text-enter → text-hold → text-exit └─> mòmies surten "del terra" (durada `spawn.duration_s` del YAML) └─> jugadors cauen del cel (delay player_spawn_delay_ms) `respawnPlayersInstant` (salt debug a sala) també crida el trigger perquè les mòmies no quedin congelades quan es curt-circuita la cinemàtica. Flag `hazards_spawn_triggered_` garanteix idempotència dins el cicle; es reseteja a `enterRoomIntro` i al `setState(ROOM_INTRO)` defensiu. Co-Authored-By: Claude Opus 4.7 (1M context) --- source/game/scenes/game_scene.cpp | 30 ++++++++++++++++++++++++++++++ source/game/scenes/game_scene.hpp | 15 +++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 022078d..fa5b6b6 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -31,6 +31,7 @@ #include "game/entities/entity_factory.hpp" #include "game/entities/entity_loader.hpp" #include "game/entities/event_bus.hpp" +#include "game/entities/hazard.hpp" #include "game/entities/player.hpp" #include "game/gameplay_config.hpp" #include "game/levels/level_manager.hpp" @@ -622,6 +623,7 @@ void GameScene::enterRoomIntro() { intro_completed_ = false; intro_spawn_elapsed_ms_ = 0; players_spawned_in_intro_.fill(false); + hazards_spawn_triggered_ = 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 @@ -674,10 +676,27 @@ void GameScene::respawnPlayersInstant() { state_ = RoomFsm::State::PLAYING; 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; Log::info("ROOM", "post-editor: spawn instantani, estat -> PLAYING"); } #endif +void GameScene::triggerHazardSpawnExit() { + int count = 0; + for (auto* entity : entity_manager_.getEntities()) { + if (auto* hazard = dynamic_cast(entity); hazard != nullptr && hazard->isSpawning()) { + hazard->triggerSpawnExit(); + ++count; + } + } + Log::info("ROOM", "INTRO: hazard spawn-exit armat ({} DynamicHazards)", count); +} + void GameScene::triggerPlayerSpawn(std::size_t slot_index) { if (slot_index >= players_.size()) { return; } if (!players_active_[slot_index]) { return; } @@ -863,6 +882,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; } auto* audio = Audio::get(); // PLAYING: arrenca la pista de joc en loop infinit. Netegem el @@ -923,6 +943,16 @@ void GameScene::updateRoomTransition(int delta_ms) { const auto NEXT = RoomFsm::advance(state_, transition_, transition_config_, delta_ms); + // ROOM_INTRO: en entrar a la fase 5 (music_tail, immediatament després + // del text-exit) alliberem els DynamicHazards del seu estat SPAWN. Les + // 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; + } + // OUTRO: arrenquem `outro_fade_` la primera vegada que entrem a la // fase de fade_out (phase 4 del timeline d'OUTRO). Si l'`advance` // ha consumit fases buides en aquest mateix frame i ja portem diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index 95ec21f..15de616 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -118,6 +118,17 @@ 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 @@ -202,6 +213,10 @@ 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}; + // 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 // cop al ctor (els punters són estables perquè `players_` no es