From b46af7ab1d6433265c1bd7cf9a1e6ce505886dc9 Mon Sep 17 00:00:00 2001 From: Gregor Klevze Date: Sun, 21 Dec 2025 17:22:30 +0100 Subject: [PATCH] removed s shortcut for sound fx toggle --- settings.ini | 2 +- src/app/TetrisApp.cpp | 3 +- src/core/application/ApplicationManager.cpp | 4 +- src/graphics/renderers/GameRenderer.cpp | 224 ++++++++++++++++++++ src/graphics/renderers/GameRenderer.h | 1 + src/graphics/renderers/UIRenderer.cpp | 2 +- src/graphics/ui/HelpOverlay.cpp | 2 +- src/states/MenuState.cpp | 2 +- src/states/PlayingState.cpp | 2 + src/ui/MenuWrappers.cpp | 2 +- 10 files changed, 236 insertions(+), 8 deletions(-) diff --git a/settings.ini b/settings.ini index 99029f7..86008f0 100644 --- a/settings.ini +++ b/settings.ini @@ -5,7 +5,7 @@ Fullscreen=1 [Audio] -Music=1 +Music=0 Sound=1 [Gameplay] diff --git a/src/app/TetrisApp.cpp b/src/app/TetrisApp.cpp index 84013ee..10432e8 100644 --- a/src/app/TetrisApp.cpp +++ b/src/app/TetrisApp.cpp @@ -775,7 +775,8 @@ void TetrisApp::Impl::runLoop() Settings::instance().setMusicEnabled(true); } } - if (e.key.scancode == SDL_SCANCODE_S) + // K: Toggle sound effects (S is reserved for co-op movement) + if (e.key.scancode == SDL_SCANCODE_K) { SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled()); Settings::instance().setSoundEnabled(SoundEffectManager::instance().isEnabled()); diff --git a/src/core/application/ApplicationManager.cpp b/src/core/application/ApplicationManager.cpp index e0fa151..5c0fde3 100644 --- a/src/core/application/ApplicationManager.cpp +++ b/src/core/application/ApplicationManager.cpp @@ -932,8 +932,8 @@ void ApplicationManager::setupStateHandlers() { m_showExitConfirmPopup = true; return; } - // S: toggle SFX enable state (music handled globally) - if (event.key.scancode == SDL_SCANCODE_S) { + // K: toggle SFX enable state (music handled globally) + if (event.key.scancode == SDL_SCANCODE_K) { SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled()); } } diff --git a/src/graphics/renderers/GameRenderer.cpp b/src/graphics/renderers/GameRenderer.cpp index 20e6cad..ed4c304 100644 --- a/src/graphics/renderers/GameRenderer.cpp +++ b/src/graphics/renderers/GameRenderer.cpp @@ -1841,6 +1841,7 @@ void GameRenderer::renderCoopPlayingState( SDL_Texture* scorePanelTex, SDL_Texture* nextPanelTex, SDL_Texture* holdPanelTex, + bool paused, float logicalW, float logicalH, float logicalScale, @@ -1962,6 +1963,229 @@ void GameRenderer::renderCoopPlayingState( SDL_RenderLine(renderer, gridX, lineY, gridX + GRID_W, lineY); } + // In-grid 3D starfield + ambient sparkles (match classic feel, per-half) + { + static Uint32 s_lastCoopSparkTick = SDL_GetTicks(); + static std::mt19937 s_coopSparkRng{ std::random_device{}() }; + static std::vector s_leftSparkles; + static std::vector s_rightSparkles; + static std::vector s_leftImpactSparks; + static std::vector s_rightImpactSparks; + static float s_leftSparkleSpawnAcc = 0.0f; + static float s_rightSparkleSpawnAcc = 0.0f; + + float halfW = GRID_W * 0.5f; + const float leftGridX = gridX; + const float rightGridX = gridX + halfW; + + Uint32 sparkNow = nowTicks; + float sparkDeltaMs = static_cast(sparkNow - s_lastCoopSparkTick); + s_lastCoopSparkTick = sparkNow; + if (sparkDeltaMs < 0.0f || sparkDeltaMs > 100.0f) { + sparkDeltaMs = 16.0f; + } + + if (!s_starfieldInitialized) { + s_inGridStarfield.init(static_cast(halfW), static_cast(GRID_H), 180); + s_starfieldInitialized = true; + } else { + s_inGridStarfield.resize(static_cast(halfW), static_cast(GRID_H)); + } + + const float deltaSeconds = std::clamp(sparkDeltaMs / 1000.0f, 0.0f, 0.033f); + s_inGridStarfield.update(deltaSeconds); + + struct MagnetInfo { bool active{false}; float x{0.0f}; float y{0.0f}; }; + auto computeMagnet = [&](CoopGame::PlayerSide side) -> MagnetInfo { + MagnetInfo info{}; + const CoopGame::Piece& activePiece = game->current(side); + const int pieceType = static_cast(activePiece.type); + if (pieceType < 0 || pieceType >= PIECE_COUNT) { + return info; + } + + float sumLocalX = 0.0f; + float sumLocalY = 0.0f; + int filledCells = 0; + const int localXOffsetCols = (side == CoopGame::PlayerSide::Right) ? 10 : 0; + for (int cy = 0; cy < 4; ++cy) { + for (int cx = 0; cx < 4; ++cx) { + if (!CoopGame::cellFilled(activePiece, cx, cy)) continue; + sumLocalX += ((activePiece.x - localXOffsetCols) + cx + 0.5f) * finalBlockSize; + sumLocalY += (activePiece.y + cy + 0.5f) * finalBlockSize; + ++filledCells; + } + } + if (filledCells <= 0) { + return info; + } + + info.active = true; + info.x = std::clamp(sumLocalX / static_cast(filledCells), 0.0f, halfW); + info.y = std::clamp(sumLocalY / static_cast(filledCells), 0.0f, GRID_H); + return info; + }; + + const MagnetInfo leftMagnet = computeMagnet(CoopGame::PlayerSide::Left); + const MagnetInfo rightMagnet = computeMagnet(CoopGame::PlayerSide::Right); + + SDL_BlendMode oldBlend = SDL_BLENDMODE_NONE; + SDL_GetRenderDrawBlendMode(renderer, &oldBlend); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + + auto drawStarfieldHalf = [&](float originX, const MagnetInfo& magnet) { + if (magnet.active) { + const float magnetStrength = finalBlockSize * 2.2f; + s_inGridStarfield.setMagnetTarget(magnet.x, magnet.y, magnetStrength); + } else { + s_inGridStarfield.clearMagnetTarget(); + } + + const float jitterAmp = 1.6f; + const float tms = static_cast(sparkNow) * 0.001f; + const float jitterX = std::sin(tms * 1.7f) * jitterAmp + std::cos(tms * 0.9f) * 0.4f; + const float jitterY = std::sin(tms * 1.1f + 3.7f) * (jitterAmp * 0.6f); + s_inGridStarfield.draw(renderer, originX + jitterX, gridY + jitterY, 0.22f, true); + }; + + drawStarfieldHalf(leftGridX, leftMagnet); + drawStarfieldHalf(rightGridX, rightMagnet); + + auto updateAndDrawSparkleLayer = [&](std::vector& sparkles, + std::vector& impactSparks, + float& spawnAcc, + const MagnetInfo& magnet, + float originX) { + if (!paused) { + const float spawnInterval = 0.08f; + spawnAcc += deltaSeconds; + while (spawnAcc >= spawnInterval) { + spawnAcc -= spawnInterval; + + Sparkle s; + bool spawnNearPiece = magnet.active && (std::uniform_real_distribution(0.0f, 1.0f)(s_coopSparkRng) > 0.35f); + + float sx = 0.0f; + float sy = 0.0f; + if (spawnNearPiece) { + float jitterX = std::uniform_real_distribution(-finalBlockSize * 1.2f, finalBlockSize * 1.2f)(s_coopSparkRng); + float jitterY = std::uniform_real_distribution(-finalBlockSize * 1.2f, finalBlockSize * 1.2f)(s_coopSparkRng); + sx = std::clamp(magnet.x + jitterX, -finalBlockSize * 2.0f, halfW + finalBlockSize * 2.0f); + sy = std::clamp(magnet.y + jitterY, -finalBlockSize * 2.0f, GRID_H + finalBlockSize * 2.0f); + } else { + float side = std::uniform_real_distribution(0.0f, 1.0f)(s_coopSparkRng); + const float borderBand = std::max(12.0f, finalBlockSize * 1.0f); + if (side < 0.2f) { + sx = std::uniform_real_distribution(-borderBand, 0.0f)(s_coopSparkRng); + sy = std::uniform_real_distribution(-borderBand, GRID_H + borderBand)(s_coopSparkRng); + } else if (side < 0.4f) { + sx = std::uniform_real_distribution(halfW, halfW + borderBand)(s_coopSparkRng); + sy = std::uniform_real_distribution(-borderBand, GRID_H + borderBand)(s_coopSparkRng); + } else if (side < 0.6f) { + sx = std::uniform_real_distribution(-borderBand, halfW + borderBand)(s_coopSparkRng); + sy = std::uniform_real_distribution(-borderBand, 0.0f)(s_coopSparkRng); + } else if (side < 0.9f) { + sx = std::uniform_real_distribution(0.0f, halfW)(s_coopSparkRng); + sy = std::uniform_real_distribution(0.0f, finalBlockSize * 2.0f)(s_coopSparkRng); + } else { + sx = std::uniform_real_distribution(-borderBand, halfW + borderBand)(s_coopSparkRng); + sy = std::uniform_real_distribution(GRID_H, GRID_H + borderBand)(s_coopSparkRng); + } + } + + s.x = sx; + s.y = sy; + float speed = std::uniform_real_distribution(10.0f, 60.0f)(s_coopSparkRng); + float ang = std::uniform_real_distribution(-3.14159f, 3.14159f)(s_coopSparkRng); + s.vx = std::cos(ang) * speed; + s.vy = std::sin(ang) * speed * 0.25f; + s.maxLifeMs = std::uniform_real_distribution(350.0f, 900.0f)(s_coopSparkRng); + s.lifeMs = s.maxLifeMs; + s.size = std::uniform_real_distribution(1.5f, 5.0f)(s_coopSparkRng); + if (std::uniform_real_distribution(0.0f, 1.0f)(s_coopSparkRng) < 0.5f) { + s.color = SDL_Color{255, 230, 180, 255}; + } else { + s.color = SDL_Color{180, 220, 255, 255}; + } + s.pulse = std::uniform_real_distribution(0.0f, 6.28f)(s_coopSparkRng); + sparkles.push_back(s); + } + } + + if (!sparkles.empty()) { + auto it = sparkles.begin(); + while (it != sparkles.end()) { + Sparkle& sp = *it; + sp.lifeMs -= sparkDeltaMs; + if (sp.lifeMs <= 0.0f) { + const int burstCount = std::uniform_int_distribution(4, 8)(s_coopSparkRng); + for (int bi = 0; bi < burstCount; ++bi) { + ImpactSpark ps; + ps.x = originX + sp.x + std::uniform_real_distribution(-2.0f, 2.0f)(s_coopSparkRng); + ps.y = gridY + sp.y + std::uniform_real_distribution(-2.0f, 2.0f)(s_coopSparkRng); + float ang = std::uniform_real_distribution(0.0f, 6.2831853f)(s_coopSparkRng); + float speed = std::uniform_real_distribution(10.0f, 120.0f)(s_coopSparkRng); + ps.vx = std::cos(ang) * speed; + ps.vy = std::sin(ang) * speed * 0.8f; + ps.maxLifeMs = std::uniform_real_distribution(220.0f, 500.0f)(s_coopSparkRng); + ps.lifeMs = ps.maxLifeMs; + ps.size = std::max(1.0f, sp.size * 0.5f); + ps.color = sp.color; + impactSparks.push_back(ps); + } + it = sparkles.erase(it); + continue; + } + + float lifeRatio = sp.lifeMs / sp.maxLifeMs; + sp.x += sp.vx * deltaSeconds; + sp.y += sp.vy * deltaSeconds; + sp.vy *= 0.995f; + sp.pulse += deltaSeconds * 8.0f; + + float pulse = 0.5f + 0.5f * std::sin(sp.pulse); + Uint8 alpha = static_cast(std::clamp(lifeRatio * pulse, 0.0f, 1.0f) * 255.0f); + SDL_SetRenderDrawColor(renderer, sp.color.r, sp.color.g, sp.color.b, alpha); + float half = sp.size * 0.5f; + SDL_FRect fr{ originX + sp.x - half, gridY + sp.y - half, sp.size, sp.size }; + SDL_RenderFillRect(renderer, &fr); + ++it; + } + } + + if (!impactSparks.empty()) { + auto it = impactSparks.begin(); + while (it != impactSparks.end()) { + ImpactSpark& spark = *it; + spark.vy += 0.00045f * sparkDeltaMs; + spark.x += spark.vx * sparkDeltaMs; + spark.y += spark.vy * sparkDeltaMs; + spark.lifeMs -= sparkDeltaMs; + if (spark.lifeMs <= 0.0f) { + it = impactSparks.erase(it); + continue; + } + float lifeRatio = spark.lifeMs / spark.maxLifeMs; + Uint8 alpha = static_cast(std::clamp(lifeRatio, 0.0f, 1.0f) * 160.0f); + SDL_SetRenderDrawColor(renderer, spark.color.r, spark.color.g, spark.color.b, alpha); + SDL_FRect sparkRect{ + spark.x - spark.size * 0.5f, + spark.y - spark.size * 0.5f, + spark.size, + spark.size * 1.4f + }; + SDL_RenderFillRect(renderer, &sparkRect); + ++it; + } + } + }; + + updateAndDrawSparkleLayer(s_leftSparkles, s_leftImpactSparks, s_leftSparkleSpawnAcc, leftMagnet, leftGridX); + updateAndDrawSparkleLayer(s_rightSparkles, s_rightImpactSparks, s_rightSparkleSpawnAcc, rightMagnet, rightGridX); + + SDL_SetRenderDrawBlendMode(renderer, oldBlend); + } + // Half-row feedback: lightly tint rows where one side is filled, brighter where both are pending clear SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); const auto& rowStates = game->rowHalfStates(); diff --git a/src/graphics/renderers/GameRenderer.h b/src/graphics/renderers/GameRenderer.h index 4143881..04a69b2 100644 --- a/src/graphics/renderers/GameRenderer.h +++ b/src/graphics/renderers/GameRenderer.h @@ -72,6 +72,7 @@ public: SDL_Texture* scorePanelTex, SDL_Texture* nextPanelTex, SDL_Texture* holdPanelTex, + bool paused, float logicalW, float logicalH, float logicalScale, diff --git a/src/graphics/renderers/UIRenderer.cpp b/src/graphics/renderers/UIRenderer.cpp index 3007b0c..f6e976b 100644 --- a/src/graphics/renderers/UIRenderer.cpp +++ b/src/graphics/renderers/UIRenderer.cpp @@ -232,6 +232,6 @@ void UIRenderer::drawSettingsPopup(SDL_Renderer* renderer, FontAtlas* font, floa // Instructions font->draw(renderer, popupX + 20, popupY + 150, "M = TOGGLE MUSIC", 1.0f, {200, 200, 220, 255}); - font->draw(renderer, popupX + 20, popupY + 170, "S = TOGGLE SOUND FX", 1.0f, {200, 200, 220, 255}); + font->draw(renderer, popupX + 20, popupY + 170, "K = TOGGLE SOUND FX", 1.0f, {200, 200, 220, 255}); font->draw(renderer, popupX + 20, popupY + 190, "ESC = CLOSE", 1.0f, {200, 200, 220, 255}); } diff --git a/src/graphics/ui/HelpOverlay.cpp b/src/graphics/ui/HelpOverlay.cpp index 356ba59..fbe2059 100644 --- a/src/graphics/ui/HelpOverlay.cpp +++ b/src/graphics/ui/HelpOverlay.cpp @@ -38,7 +38,7 @@ void Render(SDL_Renderer* renderer, FontAtlas& font, float logicalWidth, float l {"ESC", "Back / cancel current popup"}, {"F11 or ALT+ENTER", "Toggle fullscreen"}, {"M", "Mute or unmute music"}, - {"S", "Toggle sound effects"} + {"K", "Toggle sound effects"} }}; const std::array menuShortcuts{{ diff --git a/src/states/MenuState.cpp b/src/states/MenuState.cpp index 6555d16..a4c8e43 100644 --- a/src/states/MenuState.cpp +++ b/src/states/MenuState.cpp @@ -1248,7 +1248,7 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi {"ESC", "Back / cancel current popup"}, {"F11 or ALT+ENTER", "Toggle fullscreen"}, {"M", "Mute or unmute music"}, - {"S", "Toggle sound effects"} + {"K", "Toggle sound effects"} }; const ShortcutEntry menuShortcuts[] = { {"ARROW KEYS", "Navigate menu buttons"}, diff --git a/src/states/PlayingState.cpp b/src/states/PlayingState.cpp index c783780..4416b9f 100644 --- a/src/states/PlayingState.cpp +++ b/src/states/PlayingState.cpp @@ -322,6 +322,7 @@ void PlayingState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect l ctx.scorePanelTex, ctx.nextPanelTex, ctx.holdPanelTex, + paused, 1200.0f, 1000.0f, logicalScale, @@ -438,6 +439,7 @@ void PlayingState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect l ctx.scorePanelTex, ctx.nextPanelTex, ctx.holdPanelTex, + paused, 1200.0f, 1000.0f, logicalScale, diff --git a/src/ui/MenuWrappers.cpp b/src/ui/MenuWrappers.cpp index ceeb95c..121c162 100644 --- a/src/ui/MenuWrappers.cpp +++ b/src/ui/MenuWrappers.cpp @@ -83,6 +83,6 @@ void menu_drawSettingsPopup(SDL_Renderer* renderer, FontAtlas& font, bool musicE bool sfxOn = true; font.draw(renderer, popupX + 140, popupY + 100, sfxOn ? "ON" : "OFF", 1.5f, sfxOn ? SDL_Color{0,255,0,255} : SDL_Color{255,0,0,255}); font.draw(renderer, popupX + 20, popupY + 150, "M = TOGGLE MUSIC", 1.0f, SDL_Color{200,200,220,255}); - font.draw(renderer, popupX + 20, popupY + 170, "S = TOGGLE SOUND FX", 1.0f, SDL_Color{200,200,220,255}); + font.draw(renderer, popupX + 20, popupY + 170, "K = TOGGLE SOUND FX", 1.0f, SDL_Color{200,200,220,255}); font.draw(renderer, popupX + 20, popupY + 190, "ESC = CLOSE", 1.0f, SDL_Color{200,200,220,255}); }