Added intro video

This commit is contained in:
2025-12-25 09:38:06 +01:00
parent d28feb3276
commit 14cb96345c
16 changed files with 859 additions and 15 deletions

View File

@ -32,9 +32,19 @@
#include <SDL3_ttf/SDL_ttf.h>
#include "../../utils/ImagePathResolver.h"
#include <iostream>
#include "../../video/VideoPlayer.h"
#include <cmath>
#include <fstream>
#include <algorithm>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <shellapi.h>
#endif
// (Intro video playback is now handled in-process via VideoPlayer)
ApplicationManager::ApplicationManager() = default;
@ -55,7 +65,15 @@ void ApplicationManager::renderLoading(ApplicationManager* app, RenderManager& r
if (winW_actual > 0 && winH_actual > 0) app->m_starfield3D->resize(winW_actual, winH_actual);
app->m_starfield3D->draw(renderer.getSDLRenderer());
}
// If intro video is playing, render it instead of the loading UI
if (app->m_introStarted && app->m_videoPlayer) {
SDL_Renderer* sdlR = renderer.getSDLRenderer();
int winW=0, winH=0; renderer.getWindowSize(winW, winH);
app->m_videoPlayer->render(sdlR, winW, winH);
SDL_SetRenderViewport(renderer.getSDLRenderer(), nullptr);
SDL_SetRenderScale(renderer.getSDLRenderer(), 1.0f, 1.0f);
return;
}
SDL_Rect logicalVP = {0,0,0,0};
float logicalScale = 1.0f;
if (app->m_renderManager) {
@ -780,17 +798,44 @@ void ApplicationManager::setupStateHandlers() {
m_starfield3D->update(deltaTime / 1000.0f);
}
// Check if loading is complete and transition to menu
// Check if loading is complete and transition to next stage
if (m_assetManager->isLoadingComplete()) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading complete, transitioning to Menu");
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading complete, handling post-load flow");
// Update texture pointers now that assets are loaded
m_stateContext.backgroundTex = m_assetManager->getTexture("background");
m_stateContext.blocksTex = m_assetManager->getTexture("blocks");
bool ok = m_stateManager->setState(AppState::Menu);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "setState(AppState::Menu) returned %d", ok ? 1 : 0);
traceFile("- to Menu returned");
// If an intro video exists and hasn't been started, attempt to play it in-process
std::filesystem::path introPath = m_introPath;
if (!m_introStarted && std::filesystem::exists(introPath)) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Intro video found: %s", introPath.string().c_str());
try {
if (!m_videoPlayer) m_videoPlayer = std::make_unique<VideoPlayer>();
SDL_Renderer* sdlRend = (m_renderManager) ? m_renderManager->getSDLRenderer() : nullptr;
if (m_videoPlayer->open(introPath.string(), sdlRend)) {
m_introStarted = true;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Intro video started in-process");
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "VideoPlayer failed to open intro; skipping");
m_stateManager->setState(AppState::Playing);
}
} catch (const std::exception& ex) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Exception while starting VideoPlayer: %s", ex.what());
m_stateManager->setState(AppState::Playing);
}
} else if (m_introStarted) {
// Let VideoPlayer decode frames; once finished, transition to playing
if (m_videoPlayer) m_videoPlayer->update();
if (!m_videoPlayer || m_videoPlayer->isFinished()) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Intro video finished (in-process), transitioning to Playing");
m_stateManager->setState(AppState::Playing);
}
} else {
// No intro to play; transition directly to Playing
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "No intro video; transitioning to Playing");
m_stateManager->setState(AppState::Playing);
}
}
});

View File

@ -153,6 +153,11 @@ private:
float m_logoAnimCounter = 0.0f;
bool m_helpOverlayPausedGame = false;
// Intro video playback (in-process via FFmpeg)
bool m_introStarted = false;
std::string m_introPath = "assets/videos/spacetris_intro.mp4";
std::unique_ptr<class VideoPlayer> m_videoPlayer;
// Gameplay background (per-level) with fade, mirroring main.cpp behavior
SDL_Texture* m_levelBackgroundTex = nullptr;
SDL_Texture* m_nextLevelBackgroundTex = nullptr; // used during fade transitions

View File

@ -156,9 +156,19 @@ void StateManager::render(RenderManager& renderer) {
}
bool StateManager::isValidState(AppState state) const {
// All enum values are currently valid
return static_cast<int>(state) >= static_cast<int>(AppState::Loading) &&
static_cast<int>(state) <= static_cast<int>(AppState::GameOver);
switch (state) {
case AppState::Loading:
case AppState::Video:
case AppState::Menu:
case AppState::Options:
case AppState::LevelSelector:
case AppState::Playing:
case AppState::LevelSelect:
case AppState::GameOver:
return true;
default:
return false;
}
}
bool StateManager::canTransitionTo(AppState newState) const {
@ -169,6 +179,7 @@ bool StateManager::canTransitionTo(AppState newState) const {
const char* StateManager::getStateName(AppState state) const {
switch (state) {
case AppState::Loading: return "Loading";
case AppState::Video: return "Video";
case AppState::Menu: return "Menu";
case AppState::Options: return "Options";
case AppState::LevelSelector: return "LevelSelector";

View File

@ -12,6 +12,7 @@ class RenderManager;
// Application states used across the app
enum class AppState {
Loading,
Video,
Menu,
Options,
LevelSelector,