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;
|
return;
|
||||||
}
|
}
|
||||||
if (state_ == State::ROOM_OUTRO) {
|
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
|
// Placeholder fins a l'itinerari de sales: tornem a la mateixa
|
||||||
// sala. No es reinicia res — porta + sarcòfags continuen a
|
// sala. No es reinicia res — porta + sarcòfags continuen a
|
||||||
// l'estat OPEN, com era l'acord.
|
// l'estat OPEN, com era l'acord.
|
||||||
// TODO(itinerari): seleccionar la pròxima sala (incrementar
|
// TODO(itinerari): seleccionar la pròxima sala (incrementar
|
||||||
// `room_index_`, intercanviar `config_.map_yaml`, recrear
|
// `room_index_`, intercanviar `config_.map_yaml`, recrear
|
||||||
// tilemap/entitats) abans de tornar a ROOM_INTRO. La
|
// tilemap/entitats) abans de tornar a ROOM_INTRO.
|
||||||
// implementació de les sub-fases d'OUTRO arribarà al commit 4.
|
|
||||||
setState(State::ROOM_INTRO);
|
setState(State::ROOM_INTRO);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -551,43 +562,77 @@ auto GameScene::formatRoomTitle(int n) -> std::string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GameScene::renderRoomFade() {
|
void GameScene::renderRoomFade() {
|
||||||
// Fade negre opac → transparent durant ROOM_INTRO/fase 0. Pintat
|
// Fade negre sobre el món, pintat un cop els actors ja estan al
|
||||||
// sobre el món un cop els actors ja s'han dibuixat. Al commit 4
|
// canvas i abans del HUD. Dos casos:
|
||||||
// s'afegirà el fade out simètric per a ROOM_OUTRO/fase final.
|
// ROOM_INTRO/fase 0: 255 → 0 (entrada de sala, món emergent)
|
||||||
if (state_ != State::ROOM_INTRO || transition_.phase != 0) { return; }
|
// ROOM_OUTRO/fase 3: 0 → 255 (sortida de sala, món s'apaga)
|
||||||
const int DUR = transition_config_.intro_fade_in_ms;
|
int dur = 0;
|
||||||
const float T = (DUR > 0)
|
float t = 0.0F;
|
||||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(DUR), 0.0F, 1.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;
|
: 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; }
|
if (ALPHA == 0) { return; }
|
||||||
Screen::get().blendRect(0, 0, GameCanvas::WIDTH, GameCanvas::HEIGHT, 0x00000000U, ALPHA);
|
Screen::get().blendRect(0, 0, GameCanvas::WIDTH, GameCanvas::HEIGHT, 0x00000000U, ALPHA);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameScene::renderRoomText() {
|
void GameScene::renderRoomText() {
|
||||||
// Text d'entrada de sala. Visible només durant les sub-fases 2-4
|
// Text de la transició de sala. Reusa la font cachejada per a la
|
||||||
// de ROOM_INTRO (enter, hold, exit). Reusa la font cachejada per
|
// pausa (mateix tipus de lletra, mateix índex de color), i fa el
|
||||||
// a la pausa: mateix tipus de lletra i mateix índex de color.
|
// mateix patró esquerra → centre → esquerra que el text de pausa
|
||||||
if (state_ != State::ROOM_INTRO || transition_.phase < 2 || transition_.phase > 4) { return; }
|
// 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; }
|
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;
|
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;
|
const int OFF_LEFT = -W;
|
||||||
int x = GameCanvas::CENTER_X;
|
int x = GameCanvas::CENTER_X;
|
||||||
|
|
||||||
if (transition_.phase == 2) {
|
if (transition_.phase == phase_enter) {
|
||||||
const int DUR = transition_config_.intro_text_enter_ms;
|
const float T = (dur_enter > 0)
|
||||||
const float T = (DUR > 0)
|
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(dur_enter), 0.0F, 1.0F)
|
||||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(DUR), 0.0F, 1.0F)
|
|
||||||
: 1.0F;
|
: 1.0F;
|
||||||
const auto CURVE = (transition_config_.text_enter_curve != nullptr) ? transition_config_.text_enter_curve : &Easing::linear;
|
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));
|
x = Easing::lerpInt(OFF_LEFT, GameCanvas::CENTER_X, CURVE(T));
|
||||||
} else if (transition_.phase == 4) {
|
} else if (transition_.phase == phase_exit) {
|
||||||
const int DUR = transition_config_.intro_text_exit_ms;
|
const float T = (dur_exit > 0)
|
||||||
const float T = (DUR > 0)
|
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(dur_exit), 0.0F, 1.0F)
|
||||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(DUR), 0.0F, 1.0F)
|
|
||||||
: 1.0F;
|
: 1.0F;
|
||||||
const auto CURVE = (transition_config_.text_exit_curve != nullptr) ? transition_config_.text_exit_curve : &Easing::linear;
|
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));
|
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,
|
pause_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE4_FLAG,
|
||||||
x,
|
x,
|
||||||
Y,
|
Y,
|
||||||
TEXT,
|
text,
|
||||||
KERNING,
|
KERNING,
|
||||||
UiConfig::get().notificationText(),
|
UiConfig::get().notificationText(),
|
||||||
STROKE_DIST,
|
STROKE_DIST,
|
||||||
|
|||||||
Reference in new issue
Block a user