feat(room): allibera DynamicHazards del SPAWN al final del text-INTRO
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) <noreply@anthropic.com>
This commit is contained in:
1 parent
041447eaa4
commit
3b378e2b29
2 files changed
+45
No files matched your search
@@ -31,6 +31,7 @@
|
|||||||
#include "game/entities/entity_factory.hpp"
|
#include "game/entities/entity_factory.hpp"
|
||||||
#include "game/entities/entity_loader.hpp"
|
#include "game/entities/entity_loader.hpp"
|
||||||
#include "game/entities/event_bus.hpp"
|
#include "game/entities/event_bus.hpp"
|
||||||
|
#include "game/entities/hazard.hpp"
|
||||||
#include "game/entities/player.hpp"
|
#include "game/entities/player.hpp"
|
||||||
#include "game/gameplay_config.hpp"
|
#include "game/gameplay_config.hpp"
|
||||||
#include "game/levels/level_manager.hpp"
|
#include "game/levels/level_manager.hpp"
|
||||||
@@ -622,6 +623,7 @@ void GameScene::enterRoomIntro() {
|
|||||||
intro_completed_ = false;
|
intro_completed_ = false;
|
||||||
intro_spawn_elapsed_ms_ = 0;
|
intro_spawn_elapsed_ms_ = 0;
|
||||||
players_spawned_in_intro_.fill(false);
|
players_spawned_in_intro_.fill(false);
|
||||||
|
hazards_spawn_triggered_ = false;
|
||||||
|
|
||||||
// Reset visual: arrenquem el FADE_IN d'INTRO i deixem el d'OUTRO
|
// 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
|
// inactiu (es re-armarà a `setState(ROOM_OUTRO)` i, dins d'aquell
|
||||||
@@ -674,10 +676,27 @@ void GameScene::respawnPlayersInstant() {
|
|||||||
state_ = RoomFsm::State::PLAYING;
|
state_ = RoomFsm::State::PLAYING;
|
||||||
intro_completed_ = true;
|
intro_completed_ = true;
|
||||||
transition_ = {};
|
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");
|
Log::info("ROOM", "post-editor: spawn instantani, estat -> PLAYING");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void GameScene::triggerHazardSpawnExit() {
|
||||||
|
int count = 0;
|
||||||
|
for (auto* entity : entity_manager_.getEntities()) {
|
||||||
|
if (auto* hazard = dynamic_cast<DynamicHazard*>(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) {
|
void GameScene::triggerPlayerSpawn(std::size_t slot_index) {
|
||||||
if (slot_index >= players_.size()) { return; }
|
if (slot_index >= players_.size()) { return; }
|
||||||
if (!players_active_[slot_index]) { return; }
|
if (!players_active_[slot_index]) { return; }
|
||||||
@@ -863,6 +882,7 @@ void GameScene::setState(RoomFsm::State new_state) {
|
|||||||
intro_completed_ = false;
|
intro_completed_ = false;
|
||||||
intro_spawn_elapsed_ms_ = 0;
|
intro_spawn_elapsed_ms_ = 0;
|
||||||
players_spawned_in_intro_.fill(false);
|
players_spawned_in_intro_.fill(false);
|
||||||
|
hazards_spawn_triggered_ = false;
|
||||||
}
|
}
|
||||||
auto* audio = Audio::get();
|
auto* audio = Audio::get();
|
||||||
// PLAYING: arrenca la pista de joc en loop infinit. Netegem el
|
// 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);
|
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
|
// OUTRO: arrenquem `outro_fade_` la primera vegada que entrem a la
|
||||||
// fase de fade_out (phase 4 del timeline d'OUTRO). Si l'`advance`
|
// fase de fade_out (phase 4 del timeline d'OUTRO). Si l'`advance`
|
||||||
// ha consumit fases buides en aquest mateix frame i ja portem
|
// ha consumit fases buides en aquest mateix frame i ja portem
|
||||||
|
|||||||
@@ -118,6 +118,17 @@ class GameScene : public Scene {
|
|||||||
// el futur.
|
// el futur.
|
||||||
void triggerPlayerSpawn(std::size_t slot_index);
|
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
|
// 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
|
// disparat aquest cicle (`intro_completed_`), no fa res. Marca el
|
||||||
// flag, reseteja el timer post-callback i intenta spawn dels slots
|
// 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).
|
// i a `setState(ROOM_INTRO)` (camí defensiu no canònic).
|
||||||
std::array<bool, 2> players_spawned_in_intro_{false, false};
|
std::array<bool, 2> 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
|
// 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
|
// 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
|
// cop al ctor (els punters són estables perquè `players_` no es
|
||||||
|
|||||||
Reference in new issue
Block a user