From 0ab7121c5b9ae26f7a067c510650b96f584ca573 Mon Sep 17 00:00:00 2001 From: Gregor Klevze Date: Wed, 17 Dec 2025 21:05:32 +0100 Subject: [PATCH] Hold block added --- src/core/application/ApplicationManager.cpp | 2 +- src/graphics/renderers/GameRenderer.cpp | 36 +++++++++++++++++++-- src/graphics/ui/HelpOverlay.cpp | 7 ++-- src/main.cpp | 4 +-- src/states/MenuState.cpp | 3 +- src/states/PlayingState.cpp | 6 ++++ 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/core/application/ApplicationManager.cpp b/src/core/application/ApplicationManager.cpp index 30d8bc8..cd81a1e 100644 --- a/src/core/application/ApplicationManager.cpp +++ b/src/core/application/ApplicationManager.cpp @@ -362,7 +362,7 @@ bool ApplicationManager::initializeManagers() { consume = true; } - if (!consume && sc == SDL_SCANCODE_H) { + if (!consume && sc == SDL_SCANCODE_F1) { AppState currentState = m_stateManager ? m_stateManager->getState() : AppState::Loading; if (currentState != AppState::Loading) { m_showHelpOverlay = !m_showHelpOverlay; diff --git a/src/graphics/renderers/GameRenderer.cpp b/src/graphics/renderers/GameRenderer.cpp index 18504f0..326c767 100644 --- a/src/graphics/renderers/GameRenderer.cpp +++ b/src/graphics/renderers/GameRenderer.cpp @@ -1357,6 +1357,11 @@ void GameRenderer::renderPlayingState( statLines.push_back({dropStr, 370.0f, 0.7f, dropColor}); } + bool scorePanelMetricsValid = false; + float scorePanelTop = 0.0f; + float scorePanelLeftX = 0.0f; + float scorePanelWidth = 0.0f; + if (!statLines.empty()) { float statsContentTop = std::numeric_limits::max(); float statsContentBottom = std::numeric_limits::lowest(); @@ -1383,6 +1388,11 @@ void GameRenderer::renderPlayingState( SDL_SetRenderDrawColor(renderer, 12, 18, 32, 205); SDL_RenderFillRect(renderer, &statsBg); } + + scorePanelMetricsValid = true; + scorePanelTop = statsPanelTop; + scorePanelLeftX = statsPanelLeft; + scorePanelWidth = statsPanelWidth; } for (const auto& line : statLines) { @@ -1393,10 +1403,30 @@ void GameRenderer::renderPlayingState( pixelFont->draw(renderer, logicalW - 260, 10, gravityHud, 0.9f, {200, 200, 220, 255}); } - // Hold piece (if implemented) + // Hold piece (right side, above score dashboard) if (game->held().type < PIECE_COUNT) { - pixelFont->draw(renderer, statsX + 10, statsY + statsH - 80, "HOLD", 1.0f, {255, 220, 0, 255}); - drawSmallPiece(renderer, blocksTex, static_cast(game->held().type), statsX + 60, statsY + statsH - 80, finalBlockSize * 0.6f); + float holdLabelX = statsTextX; + float holdY = statsY + statsH - 80.0f; + if (scorePanelMetricsValid) { + const float holdGap = 18.0f; + const float holdBlockH = (finalBlockSize * 0.6f) * 4.0f; + holdY = scorePanelTop - holdBlockH - holdGap; + holdLabelX = statsTextX; + // Ensure HOLD block doesn't drift too far left if the score panel gets narrow. + holdLabelX = std::max(holdLabelX, scorePanelLeftX + 14.0f); + // If the score panel is extremely narrow, keep within its bounds. + holdLabelX = std::min(holdLabelX, scorePanelLeftX + std::max(0.0f, scorePanelWidth - 90.0f)); + } + + pixelFont->draw(renderer, holdLabelX, holdY, "HOLD", 1.0f, {255, 220, 0, 255}); + drawSmallPiece( + renderer, + blocksTex, + static_cast(game->held().type), + holdLabelX + 50.0f, + holdY + 2.0f, + finalBlockSize * 0.6f + ); } // Pause overlay logic moved to renderPauseOverlay diff --git a/src/graphics/ui/HelpOverlay.cpp b/src/graphics/ui/HelpOverlay.cpp index 3afdf2b..356ba59 100644 --- a/src/graphics/ui/HelpOverlay.cpp +++ b/src/graphics/ui/HelpOverlay.cpp @@ -34,7 +34,7 @@ void Render(SDL_Renderer* renderer, FontAtlas& font, float logicalWidth, float l if (!renderer) return; const std::array generalShortcuts{{ - {"H", "Toggle this help overlay"}, + {"F1", "Toggle this help overlay"}, {"ESC", "Back / cancel current popup"}, {"F11 or ALT+ENTER", "Toggle fullscreen"}, {"M", "Mute or unmute music"}, @@ -46,11 +46,12 @@ void Render(SDL_Renderer* renderer, FontAtlas& font, float logicalWidth, float l {"ENTER / SPACE", "Activate highlighted action"} }}; - const std::array gameplayShortcuts{{ + const std::array gameplayShortcuts{{ {"LEFT / RIGHT", "Move active piece"}, {"DOWN", "Soft drop (faster fall)"}, {"SPACE", "Hard drop / instant lock"}, {"UP", "Rotate clockwise"}, + {"H", "Hold / swap current piece"}, {"X", "Toggle rotation direction used by UP"}, {"P", "Pause or resume"}, {"ESC", "Open exit confirmation"} @@ -134,7 +135,7 @@ void Render(SDL_Renderer* renderer, FontAtlas& font, float logicalWidth, float l SDL_SetRenderDrawColor(renderer, 90, 110, 170, 255); SDL_RenderRect(renderer, &footerRect); - const char* closeLabel = "PRESS H OR ESC TO CLOSE"; + const char* closeLabel = "PRESS F1 OR ESC TO CLOSE"; float closeScale = fitScale(font, closeLabel, 1.0f, footerRect.w - footerPadding * 2.0f); int closeW = 0, closeH = 0; font.measure(closeLabel, closeScale, closeW, closeH); diff --git a/src/main.cpp b/src/main.cpp index 483b0bf..8a21591 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -848,8 +848,8 @@ int main(int, char **) SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled()); Settings::instance().setSoundEnabled(SoundEffectManager::instance().isEnabled()); } - // Disable H-help shortcut on the main menu; keep it elsewhere - if (e.key.scancode == SDL_SCANCODE_H && state != AppState::Loading && state != AppState::Menu) + // Help overlay toggle: F1 (keep it disabled on Loading/Menu) + if (e.key.scancode == SDL_SCANCODE_F1 && state != AppState::Loading && state != AppState::Menu) { showHelpOverlay = !showHelpOverlay; if (state == AppState::Playing) { diff --git a/src/states/MenuState.cpp b/src/states/MenuState.cpp index bfbbe40..6a8c457 100644 --- a/src/states/MenuState.cpp +++ b/src/states/MenuState.cpp @@ -1134,7 +1134,7 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi // Shortcut entries (copied from HelpOverlay) struct ShortcutEntry { const char* combo; const char* description; }; const ShortcutEntry generalShortcuts[] = { - {"H", "Toggle this help overlay"}, + {"F1", "Toggle this help overlay"}, {"ESC", "Back / cancel current popup"}, {"F11 or ALT+ENTER", "Toggle fullscreen"}, {"M", "Mute or unmute music"}, @@ -1149,6 +1149,7 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi {"DOWN", "Soft drop (faster fall)"}, {"SPACE", "Hard drop / instant lock"}, {"UP", "Rotate clockwise"}, + {"H", "Hold / swap current piece"}, {"X", "Toggle rotation direction used by UP"}, {"P", "Pause or resume"}, {"ESC", "Open exit confirmation"} diff --git a/src/states/PlayingState.cpp b/src/states/PlayingState.cpp index 2ea5417..e4de7c0 100644 --- a/src/states/PlayingState.cpp +++ b/src/states/PlayingState.cpp @@ -118,6 +118,12 @@ void PlayingState::handleEvent(const SDL_Event& e) { // Tetris controls (only when not paused) if (!ctx.game->isPaused()) { + // Hold / swap current piece (H) + if (e.key.scancode == SDL_SCANCODE_H) { + ctx.game->holdCurrent(); + return; + } + // Rotation (still event-based for precise timing) if (e.key.scancode == SDL_SCANCODE_UP) { // Use user setting to determine whether UP rotates clockwise