This commit is contained in:
2025-12-06 17:51:54 +01:00
parent f086ed3021
commit 12110bd8b4
4 changed files with 124 additions and 22 deletions

View File

@ -105,10 +105,22 @@ void SpaceWarp::spawnComet() {
float shade = randomRange(0.85f, 1.0f);
Uint8 c = static_cast<Uint8>(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 {

View File

@ -26,6 +26,77 @@
#include "../graphics/renderers/GameRenderer.h"
#include <SDL3_image/SDL_image.h>
// 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<int>(std::floor(panelTop * logicalScale + logicalVP.y));
int panelWinW = sceneW; // full width of captured scene
int panelWinH = static_cast<int>(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);

View File

@ -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);
}
}

View File

@ -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