From 12110bd8b4eff5acb1334d6cd2837ca5f98fcae9 Mon Sep 17 00:00:00 2001 From: Gregor Klevze Date: Sat, 6 Dec 2025 17:51:54 +0100 Subject: [PATCH] fixed --- src/graphics/effects/SpaceWarp.cpp | 40 +++++++++--- src/states/MenuState.cpp | 97 ++++++++++++++++++++++++++---- src/states/OptionsState.cpp | 5 +- src/states/State.h | 4 ++ 4 files changed, 124 insertions(+), 22 deletions(-) diff --git a/src/graphics/effects/SpaceWarp.cpp b/src/graphics/effects/SpaceWarp.cpp index ec8c685..e735c1e 100644 --- a/src/graphics/effects/SpaceWarp.cpp +++ b/src/graphics/effects/SpaceWarp.cpp @@ -105,10 +105,22 @@ void SpaceWarp::spawnComet() { float shade = randomRange(0.85f, 1.0f); Uint8 c = static_cast(std::clamp(220.0f + shade * 35.0f, 0.0f, 255.0f)); comet.color = SDL_Color{c, Uint8(std::min(255.0f, c * 0.95f)), 255, 255}; - comet.prevScreenX = centerX; - comet.prevScreenY = centerY; - comet.screenX = centerX; - comet.screenY = centerY; + // Initialize screen positions based on projection so the comet is not stuck at center + float sx = 0.0f, sy = 0.0f; + if (projectPoint(comet.x, comet.y, comet.z, sx, sy)) { + comet.screenX = sx; + comet.screenY = sy; + // Place prev slightly behind the head so the first frame shows motion/trail + float jitter = std::max(4.0f, comet.trailLength * 0.08f); + float ang = randomRange(0.0f, 6.28318530718f); + comet.prevScreenX = comet.screenX - std::cos(ang) * jitter; + comet.prevScreenY = comet.screenY - std::sin(ang) * jitter; + } else { + comet.prevScreenX = centerX; + comet.prevScreenY = centerY; + comet.screenX = centerX; + comet.screenY = centerY; + } comets.push_back(comet); } @@ -135,10 +147,22 @@ void SpaceWarp::respawn(WarpStar& star, bool randomDepth) { static constexpr Uint8 GRAY_SHADES[] = {160, 180, 200, 220, 240}; int idx = randomIntInclusive(rng, 0, int(std::size(GRAY_SHADES)) - 1); star.baseShade = GRAY_SHADES[idx]; - star.prevScreenX = centerX; - star.prevScreenY = centerY; - star.screenX = centerX; - star.screenY = centerY; + // Compute initial projected screen position so newly spawned stars aren't frozen at center + float sx = 0.0f, sy = 0.0f; + if (projectPoint(star.x, star.y, star.z, sx, sy)) { + star.screenX = sx; + star.screenY = sy; + // give a small previous offset so trails and motion are visible immediately + float jitter = std::max(1.0f, settings.maxTrailLength * 0.06f); + float ang = randomRange(0.0f, 6.28318530718f); + star.prevScreenX = star.screenX - std::cos(ang) * jitter; + star.prevScreenY = star.screenY - std::sin(ang) * jitter; + } else { + star.prevScreenX = centerX; + star.prevScreenY = centerY; + star.screenX = centerX; + star.screenY = centerY; + } } bool SpaceWarp::project(const WarpStar& star, float& outX, float& outY) const { diff --git a/src/states/MenuState.cpp b/src/states/MenuState.cpp index 976d437..f995bdc 100644 --- a/src/states/MenuState.cpp +++ b/src/states/MenuState.cpp @@ -26,6 +26,77 @@ #include "../graphics/renderers/GameRenderer.h" #include +// Frosted tint helper: draw a safe, inexpensive frosted overlay for the panel area. +// This avoids renderer readback / surface APIs which aren't portable across SDL3 builds. +static void renderBackdropBlur(SDL_Renderer* renderer, const SDL_Rect& logicalVP, float logicalScale, float panelTop, float panelH, SDL_Texture* sceneTex, int sceneW, int sceneH) { + if (!renderer) return; + // If we don't have a captured scene texture, fall back to the frosted tint. + if (!sceneTex || sceneW <= 0 || sceneH <= 0) { + float viewportLogicalW = (float)logicalVP.w / logicalScale; + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(renderer, 200, 210, 220, 48); + SDL_FRect base{ 0.0f, panelTop, viewportLogicalW, panelH }; + SDL_RenderFillRect(renderer, &base); + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 24); + SDL_FRect highlight{ 0.0f, panelTop, viewportLogicalW, std::max(2.0f, panelH * 0.06f) }; + SDL_RenderFillRect(renderer, &highlight); + SDL_SetRenderDrawColor(renderer, 16, 24, 32, 12); + SDL_FRect shadow{ 0.0f, panelTop + panelH - std::max(2.0f, panelH * 0.06f), viewportLogicalW, std::max(2.0f, panelH * 0.06f) }; + SDL_RenderFillRect(renderer, &shadow); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + return; + } + + // Compute source rect in scene texture pixels for the panel area + int panelWinX = 0; + int panelWinY = static_cast(std::floor(panelTop * logicalScale + logicalVP.y)); + int panelWinW = sceneW; // full width of captured scene + int panelWinH = static_cast(std::ceil(panelH * logicalScale)); + if (panelWinW <= 0 || panelWinH <= 0) return; + + // Downsample size (cheap Gaussian-ish blur via scale). + int blurW = std::max(8, panelWinW / 6); + int blurH = std::max(4, panelWinH / 6); + + // Create a small render target to draw the downsampled region into + SDL_Texture* small = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, blurW, blurH); + if (!small) { + // Fall back to tint if we can't allocate + float viewportLogicalW = (float)logicalVP.w / logicalScale; + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(renderer, 200, 210, 220, 48); + SDL_FRect base{ 0.0f, panelTop, viewportLogicalW, panelH }; + SDL_RenderFillRect(renderer, &base); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + return; + } + + // Source rectangle in the scene texture (pixel coords) as floats + SDL_FRect srcRectF{ (float)panelWinX, (float)panelWinY, (float)panelWinW, (float)panelWinH }; + + // Render the sub-region of the scene into the small texture + SDL_SetRenderTarget(renderer, small); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + + SDL_FRect smallDst{ 0.0f, 0.0f, (float)blurW, (float)blurH }; + SDL_RenderTexture(renderer, sceneTex, &srcRectF, &smallDst); + + // Reset target + SDL_SetRenderTarget(renderer, nullptr); + + // Render the small texture scaled up to the panel area with linear filtering + SDL_SetTextureBlendMode(small, SDL_BLENDMODE_BLEND); + SDL_SetTextureScaleMode(small, SDL_SCALEMODE_LINEAR); + + float viewportLogicalW = (float)logicalVP.w / logicalScale; + SDL_FRect dst{ 0.0f, panelTop, viewportLogicalW, panelH }; + SDL_RenderTexture(renderer, small, nullptr, &dst); + + // Cleanup + SDL_DestroyTexture(small); +} + MenuState::MenuState(StateContext& ctx) : State(ctx) {} void MenuState::onEnter() { @@ -72,6 +143,8 @@ void MenuState::renderMainButtonTop(SDL_Renderer* renderer, float logicalScale, float spacing = isSmall ? btnW * 1.2f : btnW * 1.15f; + + // Draw semi-transparent background panel behind the full button group (draw first so text sits on top) { float groupCenterX = btnX; @@ -81,13 +154,13 @@ void MenuState::renderMainButtonTop(SDL_Renderer* renderer, float logicalScale, float panelTop = btnY - btnH * 0.5f - 12.0f; float panelH = btnH + 24.0f; SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + // Backdrop blur pass before tint (use captured scene texture if available) + renderBackdropBlur(renderer, logicalVP, logicalScale, panelTop, panelH, ctx.sceneTex, ctx.sceneW, ctx.sceneH); // Brighter, less-opaque background to increase contrast SDL_SetRenderDrawColor(renderer, 28, 36, 46, 180); // Fill full-width background so edges are covered in fullscreen float viewportLogicalW = (float)logicalVP.w / logicalScale; - float fullLeft = 0.0f; - float fullW = viewportLogicalW; - SDL_FRect fullPanel{ fullLeft, panelTop, fullW, panelH }; + SDL_FRect fullPanel{ 0.0f, panelTop, viewportLogicalW, panelH }; SDL_RenderFillRect(renderer, &fullPanel); // Also draw the central strip to keep visual center emphasis SDL_FRect panelRect{ panelLeft, panelTop, panelRight - panelLeft, panelH }; @@ -95,9 +168,7 @@ void MenuState::renderMainButtonTop(SDL_Renderer* renderer, float logicalScale, // brighter full-width border SDL_SetRenderDrawColor(renderer, 120, 140, 160, 200); // Expand border to cover full window width (use actual viewport) - float fullLeftTop = 0.0f; - float fullWTop = viewportLogicalW; - SDL_FRect borderFull{ fullLeftTop, panelTop, fullWTop, panelH }; + SDL_FRect borderFull{ 0.0f, panelTop, viewportLogicalW, panelH }; SDL_RenderRect(renderer, &borderFull); } @@ -792,13 +863,13 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi float panelTop = btnY - btnH * 0.5f - 12.0f; float panelH = btnH + 24.0f; SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + // Backdrop blur pass before tint (use captured scene texture if available) + renderBackdropBlur(renderer, logicalVP, logicalScale, panelTop, panelH, ctx.sceneTex, ctx.sceneW, ctx.sceneH); // Brighter, less-opaque background to increase contrast (match top path) SDL_SetRenderDrawColor(renderer, 28, 36, 46, 180); // Fill full-width background so edges are covered in fullscreen float viewportLogicalW = (float)logicalVP.w / logicalScale; - float fullLeft = 0.0f; - float fullW = viewportLogicalW; - SDL_FRect fullPanel{ fullLeft, panelTop, fullW, panelH }; + SDL_FRect fullPanel{ 0.0f, panelTop, viewportLogicalW, panelH }; SDL_RenderFillRect(renderer, &fullPanel); // Also draw the central strip to keep visual center emphasis SDL_FRect panelRect{ panelLeft, panelTop, panelRight - panelLeft, panelH }; @@ -806,9 +877,7 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi // subtle border across full logical width SDL_SetRenderDrawColor(renderer, 120, 140, 160, 200); // Expand border to cover full window width (use actual viewport) - float fullLeftNormal = 0.0f; - float fullWNormal = viewportLogicalW; - SDL_FRect borderFull{ fullLeftNormal, panelTop, fullWNormal, panelH }; + SDL_FRect borderFull{ 0.0f, panelTop, viewportLogicalW, panelH }; SDL_RenderRect(renderer, &borderFull); } // Button 0 - PLAY @@ -1048,7 +1117,9 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi SDL_FRect rc{ area.x + c * (cellW + gapX), area.y + r * (cellH + gapY), cellW, cellH }; bool hovered = (levelSelected == i) || (levelHovered == i); bool selected = (ctx.startLevelSelection && *ctx.startLevelSelection == i); - SDL_Color fill = selected ? SDL_Color{255,140,40,160} : (hovered ? SDL_Color{60,80,100,120} : SDL_Color{30,40,60,110}); + // Use the project's gold/yellow tone for selected level to match UI accents + SDL_Color selectedFill = SDL_Color{255,204,0,160}; + SDL_Color fill = selected ? selectedFill : (hovered ? SDL_Color{60,80,100,120} : SDL_Color{30,40,60,110}); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(renderer, fill.r, fill.g, fill.b, fill.a); SDL_RenderFillRect(renderer, &rc); diff --git a/src/states/OptionsState.cpp b/src/states/OptionsState.cpp index 95a2799..bec188d 100644 --- a/src/states/OptionsState.cpp +++ b/src/states/OptionsState.cpp @@ -253,9 +253,12 @@ void OptionsState::toggleSmoothScroll() { } void OptionsState::exitToMenu() { + // Try a graceful fade transition if available, but always ensure we + // return to the Menu state so the UI is responsive to the user's action. if (ctx.requestFadeTransition) { ctx.requestFadeTransition(AppState::Menu); - } else if (ctx.stateManager) { + } + if (ctx.stateManager) { ctx.stateManager->setState(AppState::Menu); } } diff --git a/src/states/State.h b/src/states/State.h index 3286b72..8981c05 100644 --- a/src/states/State.h +++ b/src/states/State.h @@ -43,6 +43,10 @@ struct StateContext { SDL_Texture* mainScreenTex = nullptr; int mainScreenW = 0; int mainScreenH = 0; + // Captured full-scene texture (used by menu for backdrop blur effects) + SDL_Texture* sceneTex = nullptr; + int sceneW = 0; + int sceneH = 0; // Audio / SFX - forward declared types in main // Pointers to booleans/flags used by multiple states