feat(player): join in progress + DISABLED en agotar vidas (Fase 1)

Habilita la regla 1 de la nova FSM de joc: durant PLAYING, qualsevol
slot DISABLED pot pulsar START per entrar a partida amb spawn aeri al
seu costat de la porta i vides reseteades. Quan un jugador esgota les
vides, `respawnFromDying` el deixa a DISABLED i publica `player_disabled`
al bus; GameScene i HUD reaccionen, deixant el slot disponible per a un
nou join. Afegeix prompts al HUD ("PULSA START / PER JUGAR" o
"ESPERA / PER JUGAR") segons l'estat de la RoomFsm.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
JailDesignerandClaude Opus 4.7 committed 2026-05-08 20:04:33 +02:00
1 parent 8921b9fd0c
commit c13b11c501
12 files changed
+325 -56

No files matched your search

+38
View File
@@ -136,6 +136,18 @@ Hud::Hud(EventBus& bus, std::shared_ptr<Text> font)
Log::info("HUD", "level_complete (mode Win pendent)");
}));
// `player_disabled`: el jugador acaba d'esgotar les seves vides al
// final del cicle DYING. Tornem el bloc al render inactiu — el
// prompt textual el fixa GameScene cada frame via `setJoinPrompt`,
// així que ací només cal apagar `active`. Els camps lives/score/
// treasures es preserven per si en una versió futura volem mostrar
// el resultat final abans de tornar a zero.
subs_.push_back(bus_.subscribe("player_disabled", [this](const Event& ev) {
const std::size_t IDX = (ev.player_id == 1) ? 1U : 0U;
players_[IDX].active = false;
Log::info("HUD", "player_disabled p{}", IDX + 1);
}));
Log::info("HUD", "init: 3 blocks ready (entry {} ms)", ENTRY_MS);
}
@@ -167,6 +179,12 @@ void Hud::activatePlayer(int player_one_based) {
p.lives = DEFAULT_LIVES;
p.score = 0;
p.treasures = 0;
p.join_prompt = JoinPromptMode::NONE;
}
void Hud::setJoinPrompt(int player_one_based, JoinPromptMode mode) {
if (player_one_based < 1 || player_one_based > 2) { return; }
players_[static_cast<std::size_t>(player_one_based - 1)].join_prompt = mode;
}
auto Hud::computeOffset(const Block& b) -> SDL_FPoint {
@@ -234,6 +252,26 @@ void Hud::renderBlock(BlockId id) const {
void Hud::renderIdleSide(int x, int y, int w, const Block& blk, int player_index, const std::string& label) const {
const auto& p = players_[static_cast<std::size_t>(player_index)];
// Slot inactiu amb prompt actiu: substituïm el render de zeros pel
// missatge "PULSA START / PARA JUGAR" o "ESPERA / PARA JUGAR" segons
// el mode fixat per la GameScene cada frame. El render de zeros
// queda com a fallback (mode NONE o slot actiu).
if (!p.active && p.join_prompt != JoinPromptMode::NONE) {
const bool CAN_JOIN = (p.join_prompt == JoinPromptMode::CAN_JOIN);
const std::string L1 = Locale::get().text(
CAN_JOIN ? "hud.join_prompt_can_join_l1" : "hud.join_prompt_wait_l1");
const std::string L2 = Locale::get().text(
CAN_JOIN ? "hud.join_prompt_can_join_l2" : "hud.join_prompt_wait_l2");
const int L1_W = font_->length(L1);
const int L2_W = font_->length(L2);
const int X1 = x + ((w - L1_W) / 2);
const int X2 = x + ((w - L2_W) / 2);
font_->writeColored(X1, y + LINE1_Y, L1, UiConfig::get().hudLabel());
font_->writeColored(X2, y + LINE2_Y, L2, UiConfig::get().hudValue());
return;
}
const Uint8 SCORE_C = (blk.flash_ms_left > 0) ? UiConfig::get().hudFlash() : UiConfig::get().hudValue();
// --- Línia 1: "<label>-000000" centrada ---