feat(game-scene): ROOM_OUTRO — text de sortida i fade out del món
This commit is contained in:
1 parent
67b06c0a10
commit
4a9a037cd8
1 file changed
+70
-25
@@ -524,14 +524,25 @@ void GameScene::updateRoomTransition(int delta_ms) {
|
||||
return;
|
||||
}
|
||||
if (state_ == State::ROOM_OUTRO) {
|
||||
const std::array<int, 4> DURS = {
|
||||
transition_config_.outro_text_enter_ms,
|
||||
transition_config_.outro_text_hold_ms,
|
||||
transition_config_.outro_text_exit_ms,
|
||||
transition_config_.outro_fade_out_ms,
|
||||
};
|
||||
while (transition_.phase < static_cast<int>(DURS.size()) && transition_.elapsed_ms >= DURS[transition_.phase]) {
|
||||
transition_.elapsed_ms -= DURS[transition_.phase];
|
||||
++transition_.phase;
|
||||
}
|
||||
if (transition_.phase >= static_cast<int>(DURS.size())) {
|
||||
// Placeholder fins a l'itinerari de sales: tornem a la mateixa
|
||||
// sala. No es reinicia res — porta + sarcòfags continuen a
|
||||
// l'estat OPEN, com era l'acord.
|
||||
// TODO(itinerari): seleccionar la pròxima sala (incrementar
|
||||
// `room_index_`, intercanviar `config_.map_yaml`, recrear
|
||||
// tilemap/entitats) abans de tornar a ROOM_INTRO. La
|
||||
// implementació de les sub-fases d'OUTRO arribarà al commit 4.
|
||||
// tilemap/entitats) abans de tornar a ROOM_INTRO.
|
||||
setState(State::ROOM_INTRO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -551,43 +562,77 @@ auto GameScene::formatRoomTitle(int n) -> std::string {
|
||||
}
|
||||
|
||||
void GameScene::renderRoomFade() {
|
||||
// Fade negre opac → transparent durant ROOM_INTRO/fase 0. Pintat
|
||||
// sobre el món un cop els actors ja s'han dibuixat. Al commit 4
|
||||
// s'afegirà el fade out simètric per a ROOM_OUTRO/fase final.
|
||||
if (state_ != State::ROOM_INTRO || transition_.phase != 0) { return; }
|
||||
const int DUR = transition_config_.intro_fade_in_ms;
|
||||
const float T = (DUR > 0)
|
||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(DUR), 0.0F, 1.0F)
|
||||
// Fade negre sobre el món, pintat un cop els actors ja estan al
|
||||
// canvas i abans del HUD. Dos casos:
|
||||
// ROOM_INTRO/fase 0: 255 → 0 (entrada de sala, món emergent)
|
||||
// ROOM_OUTRO/fase 3: 0 → 255 (sortida de sala, món s'apaga)
|
||||
int dur = 0;
|
||||
float t = 0.0F;
|
||||
bool fade_in = false;
|
||||
if (state_ == State::ROOM_INTRO && transition_.phase == 0) {
|
||||
dur = transition_config_.intro_fade_in_ms;
|
||||
fade_in = true;
|
||||
} else if (state_ == State::ROOM_OUTRO && transition_.phase == 3) {
|
||||
dur = transition_config_.outro_fade_out_ms;
|
||||
fade_in = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
t = (dur > 0)
|
||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(dur), 0.0F, 1.0F)
|
||||
: 1.0F;
|
||||
const auto ALPHA = static_cast<std::uint8_t>(255.0F * (1.0F - T));
|
||||
const float ALPHA_F = fade_in ? (1.0F - t) : t;
|
||||
const auto ALPHA = static_cast<std::uint8_t>(255.0F * ALPHA_F);
|
||||
if (ALPHA == 0) { return; }
|
||||
Screen::get().blendRect(0, 0, GameCanvas::WIDTH, GameCanvas::HEIGHT, 0x00000000U, ALPHA);
|
||||
}
|
||||
|
||||
void GameScene::renderRoomText() {
|
||||
// Text d'entrada de sala. Visible només durant les sub-fases 2-4
|
||||
// de ROOM_INTRO (enter, hold, exit). Reusa la font cachejada per
|
||||
// a la pausa: mateix tipus de lletra i mateix índex de color.
|
||||
if (state_ != State::ROOM_INTRO || transition_.phase < 2 || transition_.phase > 4) { return; }
|
||||
// Text de la transició de sala. Reusa la font cachejada per a la
|
||||
// pausa (mateix tipus de lletra, mateix índex de color), i fa el
|
||||
// mateix patró esquerra → centre → esquerra que el text de pausa
|
||||
// però amb les durades pròpies de la transició.
|
||||
//
|
||||
// Mapatge per estat (només els estats amb text actiu hi entren):
|
||||
// ROOM_INTRO: phases 2..4 → enter / hold / exit; text = sala N
|
||||
// ROOM_OUTRO: phases 0..2 → enter / hold / exit; text = "fase completada"
|
||||
if (!pause_font_) { return; }
|
||||
|
||||
const std::string TEXT = formatRoomTitle(room_index_);
|
||||
std::string text;
|
||||
int phase_enter = 0;
|
||||
int phase_exit = 0;
|
||||
int dur_enter = 0;
|
||||
int dur_exit = 0;
|
||||
if (state_ == State::ROOM_INTRO && transition_.phase >= 2 && transition_.phase <= 4) {
|
||||
text = formatRoomTitle(room_index_);
|
||||
phase_enter = 2;
|
||||
phase_exit = 4;
|
||||
dur_enter = transition_config_.intro_text_enter_ms;
|
||||
dur_exit = transition_config_.intro_text_exit_ms;
|
||||
} else if (state_ == State::ROOM_OUTRO && transition_.phase >= 0 && transition_.phase <= 2) {
|
||||
text = Locale::get().text("game.room_complete");
|
||||
phase_enter = 0;
|
||||
phase_exit = 2;
|
||||
dur_enter = transition_config_.outro_text_enter_ms;
|
||||
dur_exit = transition_config_.outro_text_exit_ms;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int KERNING = 0;
|
||||
const int W = pause_font_->length(TEXT, KERNING);
|
||||
const int W = pause_font_->length(text, KERNING);
|
||||
const int OFF_LEFT = -W;
|
||||
int x = GameCanvas::CENTER_X;
|
||||
|
||||
if (transition_.phase == 2) {
|
||||
const int DUR = transition_config_.intro_text_enter_ms;
|
||||
const float T = (DUR > 0)
|
||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(DUR), 0.0F, 1.0F)
|
||||
if (transition_.phase == phase_enter) {
|
||||
const float T = (dur_enter > 0)
|
||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(dur_enter), 0.0F, 1.0F)
|
||||
: 1.0F;
|
||||
const auto CURVE = (transition_config_.text_enter_curve != nullptr) ? transition_config_.text_enter_curve : &Easing::linear;
|
||||
x = Easing::lerpInt(OFF_LEFT, GameCanvas::CENTER_X, CURVE(T));
|
||||
} else if (transition_.phase == 4) {
|
||||
const int DUR = transition_config_.intro_text_exit_ms;
|
||||
const float T = (DUR > 0)
|
||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(DUR), 0.0F, 1.0F)
|
||||
} else if (transition_.phase == phase_exit) {
|
||||
const float T = (dur_exit > 0)
|
||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(dur_exit), 0.0F, 1.0F)
|
||||
: 1.0F;
|
||||
const auto CURVE = (transition_config_.text_exit_curve != nullptr) ? transition_config_.text_exit_curve : &Easing::linear;
|
||||
x = Easing::lerpInt(GameCanvas::CENTER_X, OFF_LEFT, CURVE(T));
|
||||
@@ -599,7 +644,7 @@ void GameScene::renderRoomText() {
|
||||
pause_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE4_FLAG,
|
||||
x,
|
||||
Y,
|
||||
TEXT,
|
||||
text,
|
||||
KERNING,
|
||||
UiConfig::get().notificationText(),
|
||||
STROKE_DIST,
|
||||
|
||||
Reference in new issue
Block a user