feat(player): mort i respawn (DYING + death_spinning + lives counter)

This commit is contained in:
JailDesigner committed 2026-04-29 11:40:05 +02:00
1 parent 6397f9d4eb
commit 9f0a566ffb
11 files changed
+377 -7

No files matched your search

+21
View File
@@ -298,6 +298,14 @@ GameScene::GameScene(Config config)
std::cout << "[PLAYER HIT] player_id=" << ev.player_id
<< " dmg=" << (dmg != nullptr ? *dmg : 0) << '\n';
});
// `player_killed` el publica un hazard letal (mòmia amb
// `kills_outright`) després del `player_hit`. Aquí resolem el
// player_id a la instància concreta i disparem el cicle DYING. El
// hazard no toca cap símbol de Player directament, així els binaris
// de tests d'entitats no han d'arrossegar tot l'stack del jugador.
entity_manager_.getBus().subscribe("player_killed", [this](const Event& ev) {
dispatchPlayerKilled(ev.player_id);
});
// Polvet: precarreguem el surface del Dust una sola vegada perquè
// els spawns runtime de l'entitat no facin disc al primer salt.
@@ -602,6 +610,11 @@ void GameScene::triggerPlayerSpawn(std::size_t slot_index) {
float x = SINGLE_X;
if (BOTH_ACTIVE) { x = (slot_index == 0) ? SPAWN.p1_x : SPAWN.p2_x; }
// Memoritzem aquestes coordenades com a punt de respawn post-mort.
// El Player les usa al final del cicle DYING per teleportar-se. Les
// refresquem cada vegada que disparem un spawn aeri perquè la sala
// pot canviar de geometria de porta entre cicles.
players_[slot_index]->setSpawnPoint(x, SPAWN.y);
players_[slot_index]->spawnFromAir(x, SPAWN.y);
// Arma el reverb del primer LAND com a part de la cinemàtica
// d'INTRO. Una sola vegada per spawn aeri: el respawn post-mort
@@ -612,6 +625,14 @@ void GameScene::triggerPlayerSpawn(std::size_t slot_index) {
std::cout << "[ROOM] spawn cinematic P" << (slot_index + 1) << ": airborne entry\n";
}
void GameScene::dispatchPlayerKilled(int player_id) {
std::cout << "[PLAYER KILLED] player_id=" << player_id << '\n';
const auto IT = std::ranges::find_if(players_, [player_id](const auto& p) {
return p && p->getId() == player_id;
});
if (IT != players_.end()) { (*IT)->enterDying(); }
}
void GameScene::completeIntro() {
if (intro_completed_) { return; }
intro_completed_ = true;
+7
View File
@@ -120,6 +120,13 @@ class GameScene : public Scene {
// commuta a PLAYING — la música del joc arrenca amb el primer slot.
void completeIntro();
// Dispatch del cicle DYING al jugador identificat per `player_id`.
// Crida `Player::enterDying()` (idempotent — un segon impacte mentre
// ja és DYING és no-op). Subscriptor de l'event `player_killed`
// publicat pels hazards letals (mòmia amb `kills_outright`).
// Extret a un mètode per acotar la complexitat cognitiva del ctor.
void dispatchPlayerKilled(int player_id);
// Itera els slots actius i, si el delay configurat ha expirat des
// del callback de fi de música d'INTRO, dispara `triggerPlayerSpawn`.
// Cridada cada frame durant la fase d'spawn escalonat (entre el