feat(intro): delay de spawn independent per al segon jugador en mode BOTH
This commit is contained in:
1 parent
99eb42b319
commit
8e2865efbd
5 files changed
+53
-27
No files matched your search
@@ -12,6 +12,7 @@ room_intro:
|
|||||||
text_exit_ms: 400 # text centre → fora per l'esquerra
|
text_exit_ms: 400 # text centre → fora per l'esquerra
|
||||||
line_gap_px: -4 # px extra entre les dues línies (piràmide / sala)
|
line_gap_px: -4 # px extra entre les dues línies (piràmide / sala)
|
||||||
player_spawn_delay_ms: 2400 # ms des de l'inici d'INTRO al spawn aeri.
|
player_spawn_delay_ms: 2400 # ms des de l'inici d'INTRO al spawn aeri.
|
||||||
|
player2_spawn_delay_ms: 2700 # només mode BOTH. Trenca la simultaneïtat.
|
||||||
|
|
||||||
room_outro:
|
room_outro:
|
||||||
text_enter_ms: 400
|
text_enter_ms: 400
|
||||||
|
|||||||
@@ -363,9 +363,10 @@ GameScene::GameScene(Config config)
|
|||||||
players_.emplace_back(std::make_unique<Player>(ANIM_P2, ID_P2, p2_x, SPAWN.y, CONTROLS_P2, Map::makeInfo(), P2_PAINT));
|
players_.emplace_back(std::make_unique<Player>(ANIM_P2, ID_P2, p2_x, SPAWN.y, CONTROLS_P2, Map::makeInfo(), P2_PAINT));
|
||||||
|
|
||||||
// Tots els slots arrenquen DISABLED. Els actius els reactivarà
|
// Tots els slots arrenquen DISABLED. Els actius els reactivarà
|
||||||
// `updateRoomTransition` quan s'esgote `player_spawn_delay_ms_` dins
|
// `updateRoomTransition` quan s'esgote el delay corresponent dins
|
||||||
// de ROOM_INTRO (cinemàtica de caiguda); els inactius es queden
|
// de ROOM_INTRO (`player_spawn_delay_ms` per al primer, i
|
||||||
// DISABLED tot el cicle de sala.
|
// `player2_spawn_delay_ms` per al segon en mode BOTH). Els inactius
|
||||||
|
// es queden DISABLED tot el cicle de sala.
|
||||||
for (auto& p : players_) { p->disable(); }
|
for (auto& p : players_) { p->disable(); }
|
||||||
|
|
||||||
// PaintLayer: font única del pintat del nivell. Es crea després de
|
// PaintLayer: font única del pintat del nivell. Es crea després de
|
||||||
@@ -535,12 +536,13 @@ void GameScene::enterRoomIntro() {
|
|||||||
state_ = RoomFsm::State::ROOM_INTRO;
|
state_ = RoomFsm::State::ROOM_INTRO;
|
||||||
transition_ = {};
|
transition_ = {};
|
||||||
room_intro_elapsed_ms_ = 0;
|
room_intro_elapsed_ms_ = 0;
|
||||||
players_spawned_in_intro_ = false;
|
players_spawned_in_intro_.fill(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameScene::triggerActivePlayerSpawn() {
|
void GameScene::triggerPlayerSpawn(std::size_t slot_index) {
|
||||||
if (players_spawned_in_intro_) { return; }
|
if (slot_index >= players_spawned_in_intro_.size()) { return; }
|
||||||
players_spawned_in_intro_ = true;
|
if (players_spawned_in_intro_[slot_index]) { return; }
|
||||||
|
players_spawned_in_intro_[slot_index] = true;
|
||||||
|
|
||||||
// Recalcula spawn per si door_col canvia entre sales (avui no, però
|
// Recalcula spawn per si door_col canvia entre sales (avui no, però
|
||||||
// la lògica ja queda preparada per a l'itinerari).
|
// la lògica ja queda preparada per a l'itinerari).
|
||||||
@@ -551,14 +553,11 @@ void GameScene::triggerActivePlayerSpawn() {
|
|||||||
// amb un sol actiu, el reposicionem al centre exacte.
|
// amb un sol actiu, el reposicionem al centre exacte.
|
||||||
const float SINGLE_X = SPAWN.p1_x + static_cast<float>(Map::tile_size);
|
const float SINGLE_X = SPAWN.p1_x + static_cast<float>(Map::tile_size);
|
||||||
|
|
||||||
for (std::size_t i = 0; i < players_.size(); ++i) {
|
|
||||||
if (!players_starts_active_[i]) { continue; }
|
|
||||||
float x = SINGLE_X;
|
float x = SINGLE_X;
|
||||||
if (BOTH_ACTIVE) { x = (i == 0) ? SPAWN.p1_x : SPAWN.p2_x; }
|
if (BOTH_ACTIVE) { x = (slot_index == 0) ? SPAWN.p1_x : SPAWN.p2_x; }
|
||||||
players_[i]->spawnFromAir(x, SPAWN.y);
|
players_[slot_index]->spawnFromAir(x, SPAWN.y);
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "[ROOM] spawn cinematic: airborne entry for active players\n";
|
std::cout << "[ROOM] spawn cinematic P" << (slot_index + 1) << ": airborne entry\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameScene::setState(RoomFsm::State new_state) {
|
void GameScene::setState(RoomFsm::State new_state) {
|
||||||
@@ -570,7 +569,7 @@ void GameScene::setState(RoomFsm::State new_state) {
|
|||||||
// l'spawn aeri perquè es torne a disparar al delay configurat.
|
// l'spawn aeri perquè es torne a disparar al delay configurat.
|
||||||
if (new_state == RoomFsm::State::ROOM_INTRO) {
|
if (new_state == RoomFsm::State::ROOM_INTRO) {
|
||||||
room_intro_elapsed_ms_ = 0;
|
room_intro_elapsed_ms_ = 0;
|
||||||
players_spawned_in_intro_ = false;
|
players_spawned_in_intro_.fill(false);
|
||||||
}
|
}
|
||||||
// L'avanç de sala (OUTRO → INTRO) NO viu ací: el dispara
|
// L'avanç de sala (OUTRO → INTRO) NO viu ací: el dispara
|
||||||
// `updateRoomTransition` quan la FSM detecta el final de l'OUTRO,
|
// `updateRoomTransition` quan la FSM detecta el final de l'OUTRO,
|
||||||
@@ -587,8 +586,21 @@ void GameScene::updateRoomTransition(int delta_ms) {
|
|||||||
// respawn post-mort sense que la FSM de sala hi haja d'intervenir.
|
// respawn post-mort sense que la FSM de sala hi haja d'intervenir.
|
||||||
if (state_ == RoomFsm::State::ROOM_INTRO) {
|
if (state_ == RoomFsm::State::ROOM_INTRO) {
|
||||||
room_intro_elapsed_ms_ += delta_ms;
|
room_intro_elapsed_ms_ += delta_ms;
|
||||||
if (!players_spawned_in_intro_ && room_intro_elapsed_ms_ >= transition_config_.player_spawn_delay_ms) {
|
// Cada slot actiu pot caure en un moment distint quan els dos
|
||||||
triggerActivePlayerSpawn();
|
// estan actius (mode BOTH): P1 amb player_spawn_delay_ms i P2
|
||||||
|
// amb player2_spawn_delay_ms. En single-player (P1 o P2 sols)
|
||||||
|
// l'slot actiu fa servir player_spawn_delay_ms — el segon valor
|
||||||
|
// queda inactiu, com ha demanat el disseny.
|
||||||
|
const bool BOTH_ACTIVE = players_starts_active_[0] && players_starts_active_[1];
|
||||||
|
for (std::size_t i = 0; i < players_starts_active_.size(); ++i) {
|
||||||
|
if (!players_starts_active_[i]) { continue; }
|
||||||
|
if (players_spawned_in_intro_[i]) { continue; }
|
||||||
|
const int DELAY = (i == 1 && BOTH_ACTIVE)
|
||||||
|
? transition_config_.player2_spawn_delay_ms
|
||||||
|
: transition_config_.player_spawn_delay_ms;
|
||||||
|
if (room_intro_elapsed_ms_ >= DELAY) {
|
||||||
|
triggerPlayerSpawn(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,19 +99,21 @@ class GameScene : public Scene {
|
|||||||
std::array<bool, 2> players_starts_active_{false, false};
|
std::array<bool, 2> players_starts_active_{false, false};
|
||||||
|
|
||||||
// Mil·lisegons des de l'entrada en ROOM_INTRO, per disparar la
|
// Mil·lisegons des de l'entrada en ROOM_INTRO, per disparar la
|
||||||
// cinemàtica de spawn aeri quan s'arriba a
|
// cinemàtica de spawn aeri quan s'arriba al delay corresponent al
|
||||||
// `transition_config_.player_spawn_delay_ms`. Es reseteja a
|
// slot. Es reseteja a `setState(ROOM_INTRO)`. Cada slot té el seu
|
||||||
// `setState(ROOM_INTRO)`. `players_spawned_in_intro_` garanteix
|
// propi flag de "ja he caigut" — així podem caure en moments
|
||||||
// que el trigger només es dispara una vegada per cicle.
|
// distints en mode BOTH (`player_spawn_delay_ms` per a P1 i
|
||||||
|
// `player2_spawn_delay_ms` per a P2).
|
||||||
int room_intro_elapsed_ms_{0};
|
int room_intro_elapsed_ms_{0};
|
||||||
bool players_spawned_in_intro_{false};
|
std::array<bool, 2> players_spawned_in_intro_{false, false};
|
||||||
|
|
||||||
// Cinemàtica de spawn: posa els slots actius en AIRBORNE des d'una
|
// Cinemàtica de spawn d'un slot concret. Posa el slot indicat en
|
||||||
// altura fora de pantalla. Cridat per `updateRoomTransition` quan
|
// AIRBORNE des d'una altura fora de pantalla. Cridat per
|
||||||
// `room_intro_elapsed_ms_` supera el delay configurat. Idempotent
|
// `updateRoomTransition` quan `room_intro_elapsed_ms_` supera el
|
||||||
// (consulta `players_spawned_in_intro_`). També és el punt al qual
|
// delay específic de l'slot. Idempotent (consulta el flag corresponent
|
||||||
// es connectarà el respawn post-mort en el futur.
|
// a `players_spawned_in_intro_`). També és el punt al qual es
|
||||||
void triggerActivePlayerSpawn();
|
// connectarà el respawn post-mort en el futur.
|
||||||
|
void triggerPlayerSpawn(std::size_t slot_index);
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ namespace RoomFsm {
|
|||||||
cfg.intro_text_hold_ms = readChildInt(n, "text_hold_ms", cfg.intro_text_hold_ms);
|
cfg.intro_text_hold_ms = readChildInt(n, "text_hold_ms", cfg.intro_text_hold_ms);
|
||||||
cfg.intro_text_exit_ms = readChildInt(n, "text_exit_ms", cfg.intro_text_exit_ms);
|
cfg.intro_text_exit_ms = readChildInt(n, "text_exit_ms", cfg.intro_text_exit_ms);
|
||||||
cfg.player_spawn_delay_ms = readChildInt(n, "player_spawn_delay_ms", cfg.player_spawn_delay_ms);
|
cfg.player_spawn_delay_ms = readChildInt(n, "player_spawn_delay_ms", cfg.player_spawn_delay_ms);
|
||||||
|
// Per defecte el segon jugador cau alhora que el primer.
|
||||||
|
// El YAML pot trencar la simultaneïtat fixant un valor propi.
|
||||||
|
cfg.player2_spawn_delay_ms = readChildInt(n, "player2_spawn_delay_ms", cfg.player_spawn_delay_ms);
|
||||||
cfg.intro_text_line_gap_px = readChildInt(n, "line_gap_px", cfg.intro_text_line_gap_px);
|
cfg.intro_text_line_gap_px = readChildInt(n, "line_gap_px", cfg.intro_text_line_gap_px);
|
||||||
}
|
}
|
||||||
if (yaml.contains("room_outro")) {
|
if (yaml.contains("room_outro")) {
|
||||||
|
|||||||
@@ -57,6 +57,14 @@ namespace RoomFsm {
|
|||||||
// si el YAML no defineix res — només pensat com a degradació.
|
// si el YAML no defineix res — només pensat com a degradació.
|
||||||
int player_spawn_delay_ms{500};
|
int player_spawn_delay_ms{500};
|
||||||
|
|
||||||
|
// Delay independent per al segon jugador, **només quan els dos
|
||||||
|
// slots estan actius** (mode BOTH). En single-player (P1 o P2
|
||||||
|
// sols) s'usa sempre `player_spawn_delay_ms` per al jugador
|
||||||
|
// actiu. Si el YAML no fixa aquest valor, el `loadConfig` el
|
||||||
|
// copia de `player_spawn_delay_ms` perquè els dos players caiguin
|
||||||
|
// alhora — comportament històric del joc.
|
||||||
|
int player2_spawn_delay_ms{500};
|
||||||
|
|
||||||
// Píxels extra entre les dues línies del text d'INTRO (piràmide
|
// Píxels extra entre les dues línies del text d'INTRO (piràmide
|
||||||
// damunt, sala davall). Es sumen al `lineHeight` natural del
|
// damunt, sala davall). Es sumen al `lineHeight` natural del
|
||||||
// font per separar visualment les dues línies. 0 = stack
|
// font per separar visualment les dues línies. 0 = stack
|
||||||
|
|||||||
Reference in new issue
Block a user