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:
1 parent
6a6a1a52c7
commit
881e0ea1f6
3 files changed
+39
-39
No files matched your search
@@ -41,13 +41,13 @@ ai:
|
|||||||
direction_choice: random
|
direction_choice: random
|
||||||
initial_direction: east
|
initial_direction: east
|
||||||
|
|
||||||
# Estat SPAWN: la mòmia naix visible i quieta al pixel_rect_ inicial,
|
# Estat SPAWN: la mòmia s'instancia visible al pixel_rect_ inicial just
|
||||||
# sense col·lisió. El GameScene li arma el comptador al final del
|
# quan GameScene crida `spawnPendingActors()` (a la subfase text-exit
|
||||||
# text-exit de ROOM_INTRO; passats `duration_s` segons des d'aleshores,
|
# de ROOM_INTRO, fase 4). El `triggerSpawnExit` es dispara llavors
|
||||||
# transita a ROAMING i empieza a deambular. Sense aquest bloc, default
|
# mateix; passats `duration_s` segons transita a ROAMING i empieza a
|
||||||
# 0 ⇒ al primer update post-trigger passa directament a ACTIVE.
|
# deambular. Sense aquest bloc, default 0 ⇒ ACTIVE immediat.
|
||||||
spawn:
|
spawn:
|
||||||
duration_s: 0.6
|
duration_s: 1.0
|
||||||
stats:
|
stats:
|
||||||
speed: 30
|
speed: 30
|
||||||
damage: 1
|
damage: 1
|
||||||
|
|||||||
@@ -623,7 +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;
|
pending_actors_spawned_ = 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
|
||||||
@@ -677,24 +677,15 @@ void GameScene::respawnPlayersInstant() {
|
|||||||
intro_completed_ = true;
|
intro_completed_ = true;
|
||||||
transition_ = {};
|
transition_ = {};
|
||||||
// El salt debug a sala curt-circuit'a la cinemàtica d'INTRO i, amb
|
// 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
|
// ella, l'spawn diferit dels DynamicHazards que viu a la fase 4
|
||||||
// fase 5 d'INTRO. Si no l'armàvem ací també, les mòmies de la sala
|
// d'INTRO. Si no els instanciàvem ací també, les mòmies de la sala
|
||||||
// entrada per editor quedarien congelades.
|
// entrada per editor no apareixerien mai.
|
||||||
triggerHazardSpawnExit();
|
entity_manager_.spawnPendingActors();
|
||||||
hazards_spawn_triggered_ = true;
|
pending_actors_spawned_ = true;
|
||||||
Log::info("ROOM", "post-editor: spawn instantani, estat -> PLAYING");
|
Log::info("ROOM", "post-editor: spawn instantani, estat -> PLAYING");
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
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; }
|
||||||
@@ -844,6 +835,15 @@ void GameScene::completeIntro() {
|
|||||||
if (intro_completed_) { return; }
|
if (intro_completed_) { return; }
|
||||||
intro_completed_ = true;
|
intro_completed_ = true;
|
||||||
intro_spawn_elapsed_ms_ = 0;
|
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
|
// Spawn dels slots amb delay <= 0 (el cas habitual de P1). Els
|
||||||
// altres cauen frame a frame dins `updateRoomTransition` a mesura
|
// altres cauen frame a frame dins `updateRoomTransition` a mesura
|
||||||
// que `intro_spawn_elapsed_ms_` supera el seu delay. Així P1 cau
|
// 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_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;
|
pending_actors_spawned_ = 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
|
||||||
@@ -946,9 +946,16 @@ void GameScene::updateRoomTransition(int delta_ms) {
|
|||||||
// mòmies surten visualment "del terra" just després que el text desapareix
|
// 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
|
// 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`).
|
// 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_) {
|
// ROOM_INTRO, fase 4 = text-exit (el text està desapareixent). Instanciem
|
||||||
triggerHazardSpawnExit();
|
// els actors diferits (mòmies) — `EntityManager::spawnPendingActors` els
|
||||||
hazards_spawn_triggered_ = true;
|
// 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
|
// OUTRO: arrenquem `outro_fade_` la primera vegada que entrem a la
|
||||||
|
|||||||
@@ -118,17 +118,6 @@ 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
|
||||||
@@ -213,9 +202,13 @@ 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.
|
// Idempotència de la crida a `entity_manager_.spawnPendingActors()`
|
||||||
// Reset a `enterRoomIntro` i a `setState(ROOM_INTRO)`.
|
// dins d'un cicle d'INTRO. Es dispara una sola vegada quan
|
||||||
bool hazards_spawn_triggered_{false};
|
// 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
|
// 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
|
||||||
|
|||||||
Reference in new issue
Block a user