added transition from levels
This commit is contained in:
171
src/main.cpp
171
src/main.cpp
@ -106,6 +106,105 @@ static SDL_Texture* loadTextureFromImage(SDL_Renderer* renderer, const std::stri
|
||||
return texture;
|
||||
}
|
||||
|
||||
struct LevelBackgroundFader {
|
||||
SDL_Texture* currentTex = nullptr;
|
||||
SDL_Texture* nextTex = nullptr;
|
||||
int currentLevel = -1;
|
||||
int queuedLevel = -1;
|
||||
float fadeElapsedMs = 0.0f;
|
||||
float fadeDurationMs = 3500.0f;
|
||||
};
|
||||
|
||||
static void destroyTexture(SDL_Texture*& tex) {
|
||||
if (tex) {
|
||||
SDL_DestroyTexture(tex);
|
||||
tex = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool queueLevelBackground(LevelBackgroundFader& fader, SDL_Renderer* renderer, int level) {
|
||||
if (!renderer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
level = std::clamp(level, 0, 32);
|
||||
if (fader.currentLevel == level || fader.queuedLevel == level) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char bgPath[256];
|
||||
std::snprintf(bgPath, sizeof(bgPath), "assets/images/tetris_main_back_level%d.jpg", level);
|
||||
|
||||
SDL_Texture* newTexture = loadTextureFromImage(renderer, bgPath);
|
||||
if (!newTexture) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to queue background for level %d: %s", level, bgPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
destroyTexture(fader.nextTex);
|
||||
fader.nextTex = newTexture;
|
||||
fader.queuedLevel = level;
|
||||
fader.fadeElapsedMs = 0.0f;
|
||||
|
||||
if (!fader.currentTex) {
|
||||
fader.currentTex = fader.nextTex;
|
||||
fader.currentLevel = fader.queuedLevel;
|
||||
fader.nextTex = nullptr;
|
||||
fader.queuedLevel = -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void updateLevelBackgroundFade(LevelBackgroundFader& fader, float frameMs) {
|
||||
if (!fader.currentTex || !fader.nextTex) {
|
||||
return;
|
||||
}
|
||||
|
||||
fader.fadeElapsedMs += frameMs;
|
||||
if (fader.fadeElapsedMs >= fader.fadeDurationMs) {
|
||||
destroyTexture(fader.currentTex);
|
||||
fader.currentTex = fader.nextTex;
|
||||
fader.currentLevel = fader.queuedLevel;
|
||||
fader.nextTex = nullptr;
|
||||
fader.queuedLevel = -1;
|
||||
fader.fadeElapsedMs = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void renderLevelBackgrounds(const LevelBackgroundFader& fader, SDL_Renderer* renderer, int winW, int winH) {
|
||||
if (!renderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_FRect fullRect{0.f, 0.f, static_cast<float>(winW), static_cast<float>(winH)};
|
||||
|
||||
if (fader.currentTex && fader.nextTex) {
|
||||
const float duration = std::max(1.0f, fader.fadeDurationMs);
|
||||
const float alpha = std::clamp(fader.fadeElapsedMs / duration, 0.0f, 1.0f);
|
||||
|
||||
SDL_SetTextureAlphaMod(fader.currentTex, Uint8((1.0f - alpha) * 255.0f));
|
||||
SDL_RenderTexture(renderer, fader.currentTex, nullptr, &fullRect);
|
||||
SDL_SetTextureAlphaMod(fader.currentTex, 255);
|
||||
|
||||
SDL_SetTextureAlphaMod(fader.nextTex, Uint8(alpha * 255.0f));
|
||||
SDL_RenderTexture(renderer, fader.nextTex, nullptr, &fullRect);
|
||||
SDL_SetTextureAlphaMod(fader.nextTex, 255);
|
||||
} else if (fader.currentTex) {
|
||||
SDL_RenderTexture(renderer, fader.currentTex, nullptr, &fullRect);
|
||||
} else if (fader.nextTex) {
|
||||
SDL_RenderTexture(renderer, fader.nextTex, nullptr, &fullRect);
|
||||
}
|
||||
}
|
||||
|
||||
static void resetLevelBackgrounds(LevelBackgroundFader& fader) {
|
||||
destroyTexture(fader.currentTex);
|
||||
destroyTexture(fader.nextTex);
|
||||
fader.currentLevel = -1;
|
||||
fader.queuedLevel = -1;
|
||||
fader.fadeElapsedMs = 0.0f;
|
||||
}
|
||||
|
||||
// Hover state for level popup ( -1 = none, 0..19 = hovered level )
|
||||
// Now managed by LevelSelectorState
|
||||
|
||||
@ -501,12 +600,7 @@ int main(int, char **)
|
||||
// States should render using `ctx.backgroundTex` rather than accessing globals.
|
||||
|
||||
// Level background caching system
|
||||
SDL_Texture *levelBackgroundTex = nullptr;
|
||||
SDL_Texture *nextLevelBackgroundTex = nullptr; // used during fade transitions
|
||||
int cachedLevel = -1; // Track which level background is currently cached
|
||||
float levelFadeAlpha = 0.0f; // 0..1 blend factor where 1 means next fully visible
|
||||
const float LEVEL_FADE_DURATION = 3500.0f; // ms for fade transition (3.5s)
|
||||
float levelFadeElapsed = 0.0f;
|
||||
LevelBackgroundFader levelBackgrounds;
|
||||
|
||||
// Default start level selection: 0 (declare here so it's in scope for all handlers)
|
||||
int startLevelSelection = 0;
|
||||
@ -1180,10 +1274,7 @@ int main(int, char **)
|
||||
}
|
||||
|
||||
// Advance level background fade if a next texture is queued
|
||||
if (nextLevelBackgroundTex) {
|
||||
levelFadeElapsed += float(frameMs);
|
||||
levelFadeAlpha = std::min(1.0f, levelFadeElapsed / LEVEL_FADE_DURATION);
|
||||
}
|
||||
updateLevelBackgroundFade(levelBackgrounds, float(frameMs));
|
||||
|
||||
// Update intro animations
|
||||
if (state == AppState::Menu) {
|
||||
@ -1276,58 +1367,9 @@ int main(int, char **)
|
||||
|
||||
// Draw level-based background for gameplay, starfield for other states
|
||||
if (state == AppState::Playing) {
|
||||
// Use level-based background for gameplay with caching
|
||||
int currentLevel = game.level();
|
||||
int bgLevel = (currentLevel > 32) ? 32 : currentLevel; // Cap at level 32
|
||||
|
||||
// Only load new background if level changed
|
||||
if (cachedLevel != bgLevel) {
|
||||
// Load new level background into nextLevelBackgroundTex
|
||||
if (nextLevelBackgroundTex) { SDL_DestroyTexture(nextLevelBackgroundTex); nextLevelBackgroundTex = nullptr; }
|
||||
char bgPath[256];
|
||||
snprintf(bgPath, sizeof(bgPath), "assets/images/tetris_main_back_level%d.jpg", bgLevel);
|
||||
SDL_Texture* newLevelTex = loadTextureFromImage(renderer, bgPath);
|
||||
if (newLevelTex) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded background for level %d: %s", bgLevel, bgPath);
|
||||
nextLevelBackgroundTex = newLevelTex;
|
||||
// start fade transition
|
||||
levelFadeAlpha = 0.0f;
|
||||
levelFadeElapsed = 0.0f;
|
||||
cachedLevel = bgLevel;
|
||||
} else {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load background for level %d: %s", bgLevel, bgPath);
|
||||
// don't change textures if file missing
|
||||
cachedLevel = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw blended backgrounds if needed
|
||||
if (levelBackgroundTex || nextLevelBackgroundTex) {
|
||||
// Use actual window pixel size so backgrounds always cover full screen
|
||||
SDL_FRect fullRect = { 0, 0, (float)winW, (float)winH };
|
||||
// if fade in progress
|
||||
if (nextLevelBackgroundTex && levelFadeAlpha < 1.0f && levelBackgroundTex) {
|
||||
// draw current with inverse alpha
|
||||
SDL_SetTextureAlphaMod(levelBackgroundTex, Uint8((1.0f - levelFadeAlpha) * 255));
|
||||
SDL_RenderTexture(renderer, levelBackgroundTex, nullptr, &fullRect);
|
||||
SDL_SetTextureAlphaMod(nextLevelBackgroundTex, Uint8(levelFadeAlpha * 255));
|
||||
SDL_RenderTexture(renderer, nextLevelBackgroundTex, nullptr, &fullRect);
|
||||
// reset mods
|
||||
SDL_SetTextureAlphaMod(levelBackgroundTex, 255);
|
||||
SDL_SetTextureAlphaMod(nextLevelBackgroundTex, 255);
|
||||
}
|
||||
else if (nextLevelBackgroundTex && (!levelBackgroundTex || levelFadeAlpha >= 1.0f)) {
|
||||
// finalise swap
|
||||
if (levelBackgroundTex) { SDL_DestroyTexture(levelBackgroundTex); }
|
||||
levelBackgroundTex = nextLevelBackgroundTex;
|
||||
nextLevelBackgroundTex = nullptr;
|
||||
levelFadeAlpha = 0.0f;
|
||||
SDL_RenderTexture(renderer, levelBackgroundTex, nullptr, &fullRect);
|
||||
}
|
||||
else if (levelBackgroundTex) {
|
||||
SDL_RenderTexture(renderer, levelBackgroundTex, nullptr, &fullRect);
|
||||
}
|
||||
}
|
||||
int bgLevel = std::clamp(game.level(), 0, 32);
|
||||
queueLevelBackground(levelBackgrounds, renderer, bgLevel);
|
||||
renderLevelBackgrounds(levelBackgrounds, renderer, winW, winH);
|
||||
} else if (state == AppState::Loading) {
|
||||
// Use 3D starfield for loading screen (full screen)
|
||||
starfield3D.draw(renderer);
|
||||
@ -1648,10 +1690,7 @@ int main(int, char **)
|
||||
SDL_DestroyTexture(logoTex);
|
||||
if (backgroundTex)
|
||||
SDL_DestroyTexture(backgroundTex);
|
||||
if (nextLevelBackgroundTex)
|
||||
SDL_DestroyTexture(nextLevelBackgroundTex);
|
||||
if (levelBackgroundTex)
|
||||
SDL_DestroyTexture(levelBackgroundTex);
|
||||
resetLevelBackgrounds(levelBackgrounds);
|
||||
if (blocksTex)
|
||||
SDL_DestroyTexture(blocksTex);
|
||||
if (logoSmallTex)
|
||||
|
||||
Reference in New Issue
Block a user