From 28dd51361922ea5d11c6888ca73806c5a755bcae Mon Sep 17 00:00:00 2001 From: Gregor Klevze Date: Sat, 22 Nov 2025 20:53:26 +0100 Subject: [PATCH] fix sidebar statistics --- src/graphics/GameRenderer.cpp | 136 ++++++++++------- src/graphics/renderers/GameRenderer.cpp | 195 ++++++++++++++---------- 2 files changed, 194 insertions(+), 137 deletions(-) diff --git a/src/graphics/GameRenderer.cpp b/src/graphics/GameRenderer.cpp index 1211107..13243e4 100644 --- a/src/graphics/GameRenderer.cpp +++ b/src/graphics/GameRenderer.cpp @@ -152,13 +152,13 @@ void GameRenderer::renderPlayingState( const float NEXT_PIECE_HEIGHT = 120.0f; const float BOTTOM_MARGIN = 60.0f; - // Calculate layout dimensions + float yCursor = statsY + 44.0f; const float availableWidth = logicalW - (MIN_MARGIN * 2) - (PANEL_WIDTH * 2) - (PANEL_SPACING * 2); const float availableHeight = logicalH - TOP_MARGIN - BOTTOM_MARGIN - NEXT_PIECE_HEIGHT; const float maxBlockSizeW = availableWidth / Game::COLS; const float maxBlockSizeH = availableHeight / Game::ROWS; - const float BLOCK_SIZE = std::min(maxBlockSizeW, maxBlockSizeH); + float previewY = rowTop - 4.0f; const float finalBlockSize = std::max(20.0f, std::min(BLOCK_SIZE, 40.0f)); const float GRID_W = Game::COLS * finalBlockSize; @@ -172,17 +172,17 @@ void GameRenderer::renderPlayingState( const float totalLayoutWidth = PANEL_WIDTH + PANEL_SPACING + GRID_W + PANEL_SPACING + PANEL_WIDTH; const float layoutStartX = (logicalW - totalLayoutWidth) * 0.5f; - + float barY = previewY + previewSize + 10.0f; const float statsX = layoutStartX + contentOffsetX; const float gridX = layoutStartX + PANEL_WIDTH + PANEL_SPACING + contentOffsetX; const float scoreX = layoutStartX + PANEL_WIDTH + PANEL_SPACING + GRID_W + PANEL_SPACING + contentOffsetX; - const float gridY = contentStartY + NEXT_PIECE_HEIGHT + contentOffsetY; - - const float statsY = gridY; - const float statsW = PANEL_WIDTH; - const float statsH = GRID_H; - - // Next piece preview position + float rowBottom = percY + 14.0f; + SDL_FRect rowBg{ + previewX - 10.0f, + rowTop - 8.0f, + rowWidth + 20.0f, + rowBottom - rowTop + }; const float nextW = finalBlockSize * 4 + 20; const float nextH = finalBlockSize * 2 + 20; const float nextX = gridX + (GRID_W - nextW) * 0.5f; @@ -283,7 +283,7 @@ void GameRenderer::renderPlayingState( if (lineEffect && lineEffect->isActive()) { lineEffect->render(renderer, static_cast(gridX), static_cast(gridY), static_cast(finalBlockSize)); } - + float yCursor = statsY + 44.0f; // Draw next piece preview pixelFont->draw(renderer, nextX + 10, nextY - 20, "NEXT", 1.0f, {255, 220, 0, 255}); if (game->next().type < PIECE_COUNT) { @@ -294,66 +294,82 @@ void GameRenderer::renderPlayingState( pixelFont->draw(renderer, statsX + 10, statsY + 10, "BLOCKS", 1.0f, {255, 220, 0, 255}); const auto& blockCounts = game->getBlockCounts(); - int totalBlocks = 0; + float previewY = rowTop - 4.0f; for (int i = 0; i < PIECE_COUNT; ++i) totalBlocks += blockCounts[i]; - - const char* pieceNames[] = {"I", "O", "T", "S", "Z", "J", "L"}; - float yCursor = statsY + 52; - + + const float rowPadding = 18.0f; + const float rowWidth = statsW - rowPadding * 2.0f; + const float rowSpacing = 12.0f; + float yCursor = statsY + 44.0f; + for (int i = 0; i < PIECE_COUNT; ++i) { - float py = yCursor; - - // Draw small piece icon - float previewSize = finalBlockSize * 0.55f; - drawSmallPiece(renderer, blocksTex, static_cast(i), statsX + 18, py, previewSize); - - // Compute preview height + float rowTop = yCursor; + float previewSize = finalBlockSize * 0.52f; + float previewX = statsX + rowPadding; + float previewY = rowTop - 14.0f; + + // Determine actual piece height so bars never overlap blocks + Game::Piece previewPiece{}; + previewPiece.type = static_cast(i); + previewPiece.rot = 0; + previewPiece.x = 0; + previewPiece.y = 0; int maxCy = -1; - Game::Piece prev; - prev.type = static_cast(i); - prev.rot = 0; - prev.x = 0; - prev.y = 0; for (int cy = 0; cy < 4; ++cy) { for (int cx = 0; cx < 4; ++cx) { - if (Game::cellFilled(prev, cx, cy)) maxCy = std::max(maxCy, cy); + if (Game::cellFilled(previewPiece, cx, cy)) { + maxCy = std::max(maxCy, cy); + } } } - int tilesHigh = (maxCy >= 0 ? maxCy + 1 : 1); - float previewHeight = tilesHigh * previewSize; - - // Count display + float pieceHeight = (maxCy >= 0 ? maxCy + 1.0f : 1.0f) * previewSize; + int count = blockCounts[i]; char countStr[16]; snprintf(countStr, sizeof(countStr), "%d", count); - pixelFont->draw(renderer, statsX + statsW - 20, py + 6, countStr, 1.1f, {240, 240, 245, 255}); - - // Percentage bar + int countW = 0, countH = 0; + pixelFont->measure(countStr, 1.0f, countW, countH); + float countX = previewX + rowWidth - static_cast(countW); + float countY = previewY + 9.0f; + int perc = (totalBlocks > 0) ? int(std::round(100.0 * double(count) / double(totalBlocks))) : 0; char percStr[16]; snprintf(percStr, sizeof(percStr), "%d%%", perc); - - float barX = statsX + 12; - float barY = py + previewHeight + 18.0f; - float barW = statsW - 24; - float barH = 6; - - pixelFont->draw(renderer, barX, barY - 16, percStr, 0.8f, {230, 230, 235, 255}); - - // Progress bar - SDL_SetRenderDrawColor(renderer, 170, 170, 175, 200); + + float barX = previewX; + float barY = previewY + pieceHeight + 12.0f; + float barH = 6.0f; + float barW = rowWidth; + float percY = barY + barH + 8.0f; + float fillW = barW * (perc / 100.0f); + fillW = std::clamp(fillW, 0.0f, barW); + + float cardTop = rowTop - 14.0f; + float rowBottom = percY + 16.0f; + SDL_FRect rowBg{ + previewX - 12.0f, + cardTop, + rowWidth + 24.0f, + rowBottom - cardTop + }; + SDL_SetRenderDrawColor(renderer, 18, 26, 40, 200); + SDL_RenderFillRect(renderer, &rowBg); + SDL_SetRenderDrawColor(renderer, 70, 100, 150, 210); + SDL_RenderRect(renderer, &rowBg); + + drawSmallPiece(renderer, blocksTex, static_cast(i), previewX, previewY, previewSize); + pixelFont->draw(renderer, countX, countY, countStr, 1.0f, {245, 245, 255, 255}); + pixelFont->draw(renderer, previewX, percY, percStr, 0.8f, {215, 225, 240, 255}); + + SDL_SetRenderDrawColor(renderer, 110, 120, 140, 200); SDL_FRect track{barX, barY, barW, barH}; SDL_RenderFillRect(renderer, &track); - SDL_Color pc = COLORS[i + 1]; - SDL_SetRenderDrawColor(renderer, pc.r, pc.g, pc.b, 230); - float fillW = barW * (perc / 100.0f); - if (fillW < 0) fillW = 0; - if (fillW > barW) fillW = barW; + SDL_SetRenderDrawColor(renderer, pc.r, pc.g, pc.b, 255); SDL_FRect fill{barX, barY, fillW, barH}; SDL_RenderFillRect(renderer, &fill); - - yCursor = barY + barH + 18.0f; + + yCursor = rowBottom + rowSpacing; } // Draw score panel (right side) @@ -419,12 +435,26 @@ void GameRenderer::renderPlayingState( // Pause overlay (suppressed when requested, e.g., countdown) if (!suppressPauseVisuals && game->isPaused() && !showExitConfirmPopup) { + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + for (int i = -4; i <= 4; ++i) { + float spread = static_cast(std::abs(i)); + Uint8 alpha = Uint8(std::max(8.f, 32.f - spread * 4.f)); + SDL_SetRenderDrawColor(renderer, 24, 32, 48, alpha); + SDL_FRect blurRect{ + gridX - spread * 2.0f, + gridY - spread * 1.5f, + GRID_W + spread * 4.0f, + GRID_H + spread * 3.0f + }; + SDL_RenderFillRect(renderer, &blurRect); + } SDL_SetRenderDrawColor(renderer, 0, 0, 0, 180); SDL_FRect pauseOverlay{0, 0, logicalW, logicalH}; SDL_RenderFillRect(renderer, &pauseOverlay); pixelFont->draw(renderer, logicalW * 0.5f - 80, logicalH * 0.5f - 20, "PAUSED", 2.0f, {255, 255, 255, 255}); pixelFont->draw(renderer, logicalW * 0.5f - 120, logicalH * 0.5f + 30, "Press P to resume", 0.8f, {200, 200, 220, 255}); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); } if (showExitConfirmPopup) { diff --git a/src/graphics/renderers/GameRenderer.cpp b/src/graphics/renderers/GameRenderer.cpp index 69b80e9..47436a8 100644 --- a/src/graphics/renderers/GameRenderer.cpp +++ b/src/graphics/renderers/GameRenderer.cpp @@ -143,49 +143,49 @@ void GameRenderer::renderPlayingState( SDL_RenderFillRect(renderer, &fr); }; - // Responsive layout that scales with window size while maintaining margins - const float MIN_MARGIN = 40.0f; - const float TOP_MARGIN = 60.0f; - const float PANEL_WIDTH = 180.0f; - const float PANEL_SPACING = 30.0f; - const float NEXT_PIECE_HEIGHT = 120.0f; - const float BOTTOM_MARGIN = 60.0f; - - // Calculate layout dimensions - const float availableWidth = logicalW - (MIN_MARGIN * 2) - (PANEL_WIDTH * 2) - (PANEL_SPACING * 2); - const float availableHeight = logicalH - TOP_MARGIN - BOTTOM_MARGIN - NEXT_PIECE_HEIGHT; - - const float maxBlockSizeW = availableWidth / Game::COLS; - const float maxBlockSizeH = availableHeight / Game::ROWS; - const float BLOCK_SIZE = std::min(maxBlockSizeW, maxBlockSizeH); - const float finalBlockSize = std::max(20.0f, std::min(BLOCK_SIZE, 40.0f)); - - const float GRID_W = Game::COLS * finalBlockSize; - const float GRID_H = Game::ROWS * finalBlockSize; - - // Calculate positions - const float totalContentHeight = NEXT_PIECE_HEIGHT + GRID_H; - const float availableVerticalSpace = logicalH - TOP_MARGIN - BOTTOM_MARGIN; - const float verticalCenterOffset = (availableVerticalSpace - totalContentHeight) * 0.5f; - const float contentStartY = TOP_MARGIN + verticalCenterOffset; - - const float totalLayoutWidth = PANEL_WIDTH + PANEL_SPACING + GRID_W + PANEL_SPACING + PANEL_WIDTH; - const float layoutStartX = (logicalW - totalLayoutWidth) * 0.5f; - - const float statsX = layoutStartX + contentOffsetX; - const float gridX = layoutStartX + PANEL_WIDTH + PANEL_SPACING + contentOffsetX; - const float scoreX = layoutStartX + PANEL_WIDTH + PANEL_SPACING + GRID_W + PANEL_SPACING + contentOffsetX; - const float gridY = contentStartY + NEXT_PIECE_HEIGHT + contentOffsetY; - - const float statsY = gridY; - const float statsW = PANEL_WIDTH; - const float statsH = GRID_H; - - // Next piece preview position - const float nextW = finalBlockSize * 4 + 20; - const float nextH = finalBlockSize * 2 + 20; - const float nextX = gridX + (GRID_W - nextW) * 0.5f; - const float nextY = contentStartY + contentOffsetY; + // Responsive layout that scales with window size while maintaining margins + const float MIN_MARGIN = 40.0f; + const float TOP_MARGIN = 60.0f; + const float PANEL_WIDTH = 180.0f; + const float PANEL_SPACING = 30.0f; + const float NEXT_PIECE_HEIGHT = 120.0f; + const float BOTTOM_MARGIN = 60.0f; + + // Calculate layout dimensions + const float availableWidth = logicalW - (MIN_MARGIN * 2) - (PANEL_WIDTH * 2) - (PANEL_SPACING * 2); + const float availableHeight = logicalH - TOP_MARGIN - BOTTOM_MARGIN - NEXT_PIECE_HEIGHT; + + const float maxBlockSizeW = availableWidth / Game::COLS; + const float maxBlockSizeH = availableHeight / Game::ROWS; + const float BLOCK_SIZE = std::min(maxBlockSizeW, maxBlockSizeH); + const float finalBlockSize = std::max(20.0f, std::min(BLOCK_SIZE, 40.0f)); + + const float GRID_W = Game::COLS * finalBlockSize; + const float GRID_H = Game::ROWS * finalBlockSize; + + // Calculate positions + const float totalContentHeight = NEXT_PIECE_HEIGHT + GRID_H; + const float availableVerticalSpace = logicalH - TOP_MARGIN - BOTTOM_MARGIN; + const float verticalCenterOffset = (availableVerticalSpace - totalContentHeight) * 0.5f; + const float contentStartY = TOP_MARGIN + verticalCenterOffset; + + const float totalLayoutWidth = PANEL_WIDTH + PANEL_SPACING + GRID_W + PANEL_SPACING + PANEL_WIDTH; + const float layoutStartX = (logicalW - totalLayoutWidth) * 0.5f; + + const float statsX = layoutStartX + contentOffsetX; + const float gridX = layoutStartX + PANEL_WIDTH + PANEL_SPACING + contentOffsetX; + const float scoreX = layoutStartX + PANEL_WIDTH + PANEL_SPACING + GRID_W + PANEL_SPACING + contentOffsetX; + const float gridY = contentStartY + NEXT_PIECE_HEIGHT + contentOffsetY; + + const float statsY = gridY; + const float statsW = PANEL_WIDTH; + const float statsH = GRID_H; + + // Next piece preview position + const float nextW = finalBlockSize * 4 + 20; + const float nextH = finalBlockSize * 2 + 20; + const float nextX = gridX + (GRID_W - nextW) * 0.5f; + const float nextY = contentStartY + contentOffsetY; // Handle line clearing effects if (game->hasCompletedLines() && lineEffect && !lineEffect->isActive()) { @@ -291,68 +291,80 @@ void GameRenderer::renderPlayingState( // Draw block statistics (left panel) pixelFont->draw(renderer, statsX + 10, statsY + 10, "BLOCKS", 1.0f, {255, 220, 0, 255}); - + const auto& blockCounts = game->getBlockCounts(); int totalBlocks = 0; for (int i = 0; i < PIECE_COUNT; ++i) totalBlocks += blockCounts[i]; - - const char* pieceNames[] = {"I", "O", "T", "S", "Z", "J", "L"}; - float yCursor = statsY + 52; - + + const float rowPadding = 18.0f; + const float rowWidth = statsW - rowPadding * 2.0f; + const float rowSpacing = 12.0f; + float yCursor = statsY + 44.0f; + for (int i = 0; i < PIECE_COUNT; ++i) { - float py = yCursor; - - // Draw small piece icon - float previewSize = finalBlockSize * 0.55f; - drawSmallPiece(renderer, blocksTex, static_cast(i), statsX + 18, py, previewSize); - - // Compute preview height + float rowTop = yCursor; + float previewSize = finalBlockSize * 0.52f; + float previewX = statsX + rowPadding; + float previewY = rowTop - 14.0f; + + // Determine actual preview height to keep bars below the blocks + Game::Piece previewPiece{}; + previewPiece.type = static_cast(i); int maxCy = -1; - Game::Piece prev; - prev.type = static_cast(i); - prev.rot = 0; - prev.x = 0; - prev.y = 0; for (int cy = 0; cy < 4; ++cy) { for (int cx = 0; cx < 4; ++cx) { - if (Game::cellFilled(prev, cx, cy)) maxCy = std::max(maxCy, cy); + if (Game::cellFilled(previewPiece, cx, cy)) { + maxCy = std::max(maxCy, cy); + } } } - int tilesHigh = (maxCy >= 0 ? maxCy + 1 : 1); - float previewHeight = tilesHigh * previewSize; - - // Count display + float pieceHeight = (maxCy >= 0 ? maxCy + 1.0f : 1.0f) * previewSize; + int count = blockCounts[i]; char countStr[16]; snprintf(countStr, sizeof(countStr), "%d", count); - pixelFont->draw(renderer, statsX + statsW - 20, py + 6, countStr, 1.1f, {240, 240, 245, 255}); - - // Percentage bar + int countW = 0, countH = 0; + pixelFont->measure(countStr, 1.0f, countW, countH); + float countX = previewX + rowWidth - static_cast(countW); + float countY = previewY + 9.0f; + int perc = (totalBlocks > 0) ? int(std::round(100.0 * double(count) / double(totalBlocks))) : 0; char percStr[16]; snprintf(percStr, sizeof(percStr), "%d%%", perc); - - float barX = statsX + 12; - float barY = py + previewHeight + 18.0f; - float barW = statsW - 24; - float barH = 6; - - pixelFont->draw(renderer, barX, barY - 16, percStr, 0.8f, {230, 230, 235, 255}); - - // Progress bar - SDL_SetRenderDrawColor(renderer, 170, 170, 175, 200); + + float barX = previewX; + float barY = previewY + pieceHeight + 12.0f; + float barH = 6.0f; + float barW = rowWidth; + float percY = barY + barH + 8.0f; + + float rowBottom = percY + 16.0f; + SDL_FRect rowBg{ + previewX - 12.0f, + rowTop - 14.0f, + rowWidth + 24.0f, + rowBottom - (rowTop - 14.0f) + }; + SDL_SetRenderDrawColor(renderer, 18, 26, 40, 200); + SDL_RenderFillRect(renderer, &rowBg); + SDL_SetRenderDrawColor(renderer, 70, 100, 150, 210); + SDL_RenderRect(renderer, &rowBg); + + drawSmallPiece(renderer, blocksTex, static_cast(i), previewX, previewY, previewSize); + pixelFont->draw(renderer, countX, countY, countStr, 1.0f, {245, 245, 255, 255}); + pixelFont->draw(renderer, previewX, percY, percStr, 0.8f, {215, 225, 240, 255}); + + SDL_SetRenderDrawColor(renderer, 110, 120, 140, 200); SDL_FRect track{barX, barY, barW, barH}; SDL_RenderFillRect(renderer, &track); - SDL_Color pc = COLORS[i + 1]; - SDL_SetRenderDrawColor(renderer, pc.r, pc.g, pc.b, 230); + SDL_SetRenderDrawColor(renderer, pc.r, pc.g, pc.b, 255); float fillW = barW * (perc / 100.0f); - if (fillW < 0) fillW = 0; - if (fillW > barW) fillW = barW; + fillW = std::clamp(fillW, 0.0f, barW); SDL_FRect fill{barX, barY, fillW, barH}; SDL_RenderFillRect(renderer, &fill); - - yCursor = barY + barH + 18.0f; + + yCursor = rowBottom + rowSpacing; } // Draw score panel (right side) @@ -418,12 +430,27 @@ void GameRenderer::renderPlayingState( // Pause overlay (skip when visuals are suppressed, e.g., countdown) if (!suppressPauseVisuals && game->isPaused() && !showExitConfirmPopup) { + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + for (int i = -4; i <= 4; ++i) { + float spread = static_cast(std::abs(i)); + Uint8 alpha = Uint8(std::max(8.f, 32.f - spread * 4.f)); + SDL_SetRenderDrawColor(renderer, 24, 32, 48, alpha); + SDL_FRect blurRect{ + gridX - spread * 2.0f, + gridY - spread * 1.5f, + GRID_W + spread * 4.0f, + GRID_H + spread * 3.0f + }; + SDL_RenderFillRect(renderer, &blurRect); + } + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 180); SDL_FRect pauseOverlay{0, 0, logicalW, logicalH}; SDL_RenderFillRect(renderer, &pauseOverlay); pixelFont->draw(renderer, logicalW * 0.5f - 80, logicalH * 0.5f - 20, "PAUSED", 2.0f, {255, 255, 255, 255}); pixelFont->draw(renderer, logicalW * 0.5f - 120, logicalH * 0.5f + 30, "Press P to resume", 0.8f, {200, 200, 220, 255}); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); } // Exit confirmation popup styled like other retro panels