Added intro video
This commit is contained in:
@ -60,6 +60,7 @@
|
||||
#include "states/MenuState.h"
|
||||
#include "states/OptionsState.h"
|
||||
#include "states/PlayingState.h"
|
||||
#include "states/VideoState.h"
|
||||
#include "states/State.h"
|
||||
|
||||
#include "ui/BottomMenu.h"
|
||||
@ -310,11 +311,21 @@ struct TetrisApp::Impl {
|
||||
std::unique_ptr<StateManager> stateMgr;
|
||||
StateContext ctx{};
|
||||
std::unique_ptr<LoadingState> loadingState;
|
||||
std::unique_ptr<VideoState> videoState;
|
||||
std::unique_ptr<MenuState> menuState;
|
||||
std::unique_ptr<OptionsState> optionsState;
|
||||
std::unique_ptr<LevelSelectorState> levelSelectorState;
|
||||
std::unique_ptr<PlayingState> playingState;
|
||||
|
||||
// Startup fade-in overlay (used after intro video).
|
||||
bool startupFadeActive = false;
|
||||
float startupFadeAlpha = 0.0f; // 0..1 black overlay strength
|
||||
double startupFadeClockMs = 0.0;
|
||||
static constexpr double STARTUP_FADE_IN_MS = 650.0;
|
||||
|
||||
// Intro video path.
|
||||
std::string introVideoPath = "assets/videos/spacetris_intro.mp4";
|
||||
|
||||
int init();
|
||||
void runLoop();
|
||||
void shutdown();
|
||||
@ -671,7 +682,11 @@ int TetrisApp::Impl::init()
|
||||
};
|
||||
ctx.requestFadeTransition = requestStateFade;
|
||||
|
||||
ctx.startupFadeActive = &startupFadeActive;
|
||||
ctx.startupFadeAlpha = &startupFadeAlpha;
|
||||
|
||||
loadingState = std::make_unique<LoadingState>(ctx);
|
||||
videoState = std::make_unique<VideoState>(ctx);
|
||||
menuState = std::make_unique<MenuState>(ctx);
|
||||
optionsState = std::make_unique<OptionsState>(ctx);
|
||||
levelSelectorState = std::make_unique<LevelSelectorState>(ctx);
|
||||
@ -681,6 +696,20 @@ int TetrisApp::Impl::init()
|
||||
stateMgr->registerOnEnter(AppState::Loading, [this](){ loadingState->onEnter(); loadingStarted.store(true); });
|
||||
stateMgr->registerOnExit(AppState::Loading, [this](){ loadingState->onExit(); });
|
||||
|
||||
stateMgr->registerHandler(AppState::Video, [this](const SDL_Event& e){ if (videoState) videoState->handleEvent(e); });
|
||||
stateMgr->registerOnEnter(AppState::Video, [this]() {
|
||||
if (!videoState) return;
|
||||
const bool ok = videoState->begin(renderer, introVideoPath);
|
||||
if (!ok) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Intro video unavailable; skipping to Menu");
|
||||
state = AppState::Menu;
|
||||
stateMgr->setState(state);
|
||||
return;
|
||||
}
|
||||
videoState->onEnter();
|
||||
});
|
||||
stateMgr->registerOnExit(AppState::Video, [this](){ if (videoState) videoState->onExit(); });
|
||||
|
||||
stateMgr->registerHandler(AppState::Menu, [this](const SDL_Event& e){ menuState->handleEvent(e); });
|
||||
stateMgr->registerOnEnter(AppState::Menu, [this](){ menuState->onEnter(); });
|
||||
stateMgr->registerOnExit(AppState::Menu, [this](){ menuState->onExit(); });
|
||||
@ -832,7 +861,7 @@ void TetrisApp::Impl::runLoop()
|
||||
Settings::instance().setSoundEnabled(SoundEffectManager::instance().isEnabled());
|
||||
}
|
||||
const bool helpToggleKey =
|
||||
(e.key.scancode == SDL_SCANCODE_F1 && state != AppState::Loading && state != AppState::Menu);
|
||||
(e.key.scancode == SDL_SCANCODE_F1 && state != AppState::Loading && state != AppState::Video && state != AppState::Menu);
|
||||
if (helpToggleKey)
|
||||
{
|
||||
showHelpOverlay = !showHelpOverlay;
|
||||
@ -1168,6 +1197,21 @@ void TetrisApp::Impl::runLoop()
|
||||
if (frameMs > 100.0) frameMs = 100.0;
|
||||
gameplayBackgroundClockMs += frameMs;
|
||||
|
||||
if (startupFadeActive) {
|
||||
if (startupFadeClockMs <= 0.0) {
|
||||
startupFadeClockMs = STARTUP_FADE_IN_MS;
|
||||
startupFadeAlpha = 1.0f;
|
||||
}
|
||||
startupFadeClockMs -= frameMs;
|
||||
if (startupFadeClockMs <= 0.0) {
|
||||
startupFadeClockMs = 0.0;
|
||||
startupFadeAlpha = 0.0f;
|
||||
startupFadeActive = false;
|
||||
} else {
|
||||
startupFadeAlpha = float(std::clamp(startupFadeClockMs / STARTUP_FADE_IN_MS, 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
auto clearChallengeStory = [this]() {
|
||||
challengeStoryText.clear();
|
||||
challengeStoryLevel = 0;
|
||||
@ -1810,7 +1854,15 @@ void TetrisApp::Impl::runLoop()
|
||||
if (totalTasks > 0) {
|
||||
loadingProgress = std::min(1.0, double(doneTasks) / double(totalTasks));
|
||||
if (loadingProgress >= 1.0 && musicLoaded) {
|
||||
state = AppState::Menu;
|
||||
startupFadeActive = false;
|
||||
startupFadeAlpha = 0.0f;
|
||||
startupFadeClockMs = 0.0;
|
||||
|
||||
if (std::filesystem::exists(introVideoPath)) {
|
||||
state = AppState::Video;
|
||||
} else {
|
||||
state = AppState::Menu;
|
||||
}
|
||||
stateMgr->setState(state);
|
||||
}
|
||||
} else {
|
||||
@ -1838,7 +1890,15 @@ void TetrisApp::Impl::runLoop()
|
||||
if (loadingProgress > 0.99) loadingProgress = 1.0;
|
||||
if (!musicLoaded && timeProgress >= 0.1) loadingProgress = 1.0;
|
||||
if (loadingProgress >= 1.0 && musicLoaded) {
|
||||
state = AppState::Menu;
|
||||
startupFadeActive = false;
|
||||
startupFadeAlpha = 0.0f;
|
||||
startupFadeClockMs = 0.0;
|
||||
|
||||
if (std::filesystem::exists(introVideoPath)) {
|
||||
state = AppState::Video;
|
||||
} else {
|
||||
state = AppState::Menu;
|
||||
}
|
||||
stateMgr->setState(state);
|
||||
}
|
||||
}
|
||||
@ -1905,6 +1965,9 @@ void TetrisApp::Impl::runLoop()
|
||||
case AppState::Loading:
|
||||
loadingState->update(frameMs);
|
||||
break;
|
||||
case AppState::Video:
|
||||
if (videoState) videoState->update(frameMs);
|
||||
break;
|
||||
case AppState::Menu:
|
||||
menuState->update(frameMs);
|
||||
break;
|
||||
@ -2207,6 +2270,11 @@ void TetrisApp::Impl::runLoop()
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AppState::Video:
|
||||
if (videoState) {
|
||||
videoState->render(renderer, logicalScale, logicalVP);
|
||||
}
|
||||
break;
|
||||
case AppState::Menu:
|
||||
if (!mainScreenTex) {
|
||||
mainScreenTex = assetLoader.getTexture(Assets::MAIN_SCREEN);
|
||||
@ -2600,6 +2668,17 @@ void TetrisApp::Impl::runLoop()
|
||||
HelpOverlay::Render(renderer, pixelFont, LOGICAL_W, LOGICAL_H, contentOffsetX, contentOffsetY);
|
||||
}
|
||||
|
||||
if (startupFadeActive && startupFadeAlpha > 0.0f) {
|
||||
SDL_SetRenderViewport(renderer, nullptr);
|
||||
SDL_SetRenderScale(renderer, 1.0f, 1.0f);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
const Uint8 a = (Uint8)std::clamp((int)std::lround(startupFadeAlpha * 255.0f), 0, 255);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, a);
|
||||
SDL_FRect full{0.f, 0.f, (float)winW, (float)winH};
|
||||
SDL_RenderFillRect(renderer, &full);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
|
||||
}
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
SDL_SetRenderScale(renderer, 1.f, 1.f);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user