fixed statistics panel

This commit is contained in:
2025-12-07 10:29:20 +01:00
parent 262ae49496
commit 59dc3a1638
2 changed files with 78 additions and 37 deletions

View File

@ -237,10 +237,22 @@ void GameRenderer::renderPlayingState(
drawRectWithOffset(gridX - 1 - contentOffsetX, gridY - 1 - contentOffsetY, GRID_W + 2, GRID_H + 2, {60, 80, 160, 255}); drawRectWithOffset(gridX - 1 - contentOffsetX, gridY - 1 - contentOffsetY, GRID_W + 2, GRID_H + 2, {60, 80, 160, 255});
drawRectWithOffset(gridX - contentOffsetX, gridY - contentOffsetY, GRID_W, GRID_H, {20, 25, 35, 255}); drawRectWithOffset(gridX - contentOffsetX, gridY - contentOffsetY, GRID_W, GRID_H, {20, 25, 35, 255});
// Draw panel backgrounds // Draw stats panel backdrop using the same art as the score panel
SDL_SetRenderDrawColor(renderer, 10, 15, 25, 160); const float blocksPanelPadLeft = 34.0f;
SDL_FRect lbg{statsX - 16, gridY - 10, statsW + 32, GRID_H + 20}; const float blocksPanelPadRight = 10.0f;
SDL_RenderFillRect(renderer, &lbg); const float blocksPanelPadY = 26.0f;
SDL_FRect blocksPanelBg{
statsX - blocksPanelPadLeft,
gridY - blocksPanelPadY,
statsW + blocksPanelPadLeft + blocksPanelPadRight,
GRID_H + blocksPanelPadY * 2.0f
};
if (scorePanelTex) {
SDL_RenderTexture(renderer, scorePanelTex, nullptr, &blocksPanelBg);
} else {
SDL_SetRenderDrawColor(renderer, 12, 18, 32, 205);
SDL_RenderFillRect(renderer, &blocksPanelBg);
}
// Draw grid lines // Draw grid lines
SDL_SetRenderDrawColor(renderer, 40, 45, 60, 255); SDL_SetRenderDrawColor(renderer, 40, 45, 60, 255);
@ -302,10 +314,6 @@ void GameRenderer::renderPlayingState(
s_inGridStarfield.draw(renderer, gridX, gridY, 0.22f, true); s_inGridStarfield.draw(renderer, gridX, gridY, 0.22f, true);
SDL_SetRenderDrawBlendMode(renderer, oldBlend); SDL_SetRenderDrawBlendMode(renderer, oldBlend);
// Draw block statistics panel border
drawRectWithOffset(statsX - 3 - contentOffsetX, statsY - 3 - contentOffsetY, statsW + 6, statsH + 6, {100, 120, 200, 255});
drawRectWithOffset(statsX - contentOffsetX, statsY - contentOffsetY, statsW, statsH, {30, 35, 50, 255});
// Draw next piece preview panel border // Draw next piece preview panel border
drawRectWithOffset(nextX - 3 - contentOffsetX, nextY - 3 - contentOffsetY, nextW + 6, nextH + 6, {100, 120, 200, 255}); drawRectWithOffset(nextX - 3 - contentOffsetX, nextY - 3 - contentOffsetY, nextW + 6, nextH + 6, {100, 120, 200, 255});
drawRectWithOffset(nextX - contentOffsetX, nextY - contentOffsetY, nextW, nextH, {30, 35, 50, 255}); drawRectWithOffset(nextX - contentOffsetX, nextY - contentOffsetY, nextW, nextH, {30, 35, 50, 255});
@ -565,24 +573,23 @@ void GameRenderer::renderPlayingState(
} }
// Draw block statistics (left panel) // Draw block statistics (left panel)
pixelFont->draw(renderer, statsX + 10, statsY + 10, "BLOCKS", 1.0f, {255, 220, 0, 255});
const auto& blockCounts = game->getBlockCounts(); const auto& blockCounts = game->getBlockCounts();
int totalBlocks = 0; int totalBlocks = 0;
for (int i = 0; i < PIECE_COUNT; ++i) totalBlocks += blockCounts[i]; for (int i = 0; i < PIECE_COUNT; ++i) totalBlocks += blockCounts[i];
const float rowPadding = 18.0f; const float rowPadding = 34.0f;
const float rowWidth = statsW - rowPadding * 2.0f; const float rowWidth = statsW - rowPadding * 2.0f;
const float rowSpacing = 12.0f; const float rowSpacing = 18.0f;
float yCursor = statsY + 44.0f; float yCursor = statsY + 34.0f;
for (int i = 0; i < PIECE_COUNT; ++i) { for (int i = 0; i < PIECE_COUNT; ++i) {
float rowTop = yCursor; float rowTop = yCursor;
float previewSize = finalBlockSize * 0.52f; float rowLeft = statsX + rowPadding;
float previewX = statsX + rowPadding; float rowRight = rowLeft + rowWidth;
float previewY = rowTop - 14.0f; float previewSize = finalBlockSize * 0.5f;
float previewX = rowLeft;
float previewY = rowTop - 10.0f;
// Determine actual preview height to keep bars below the blocks
Game::Piece previewPiece{}; Game::Piece previewPiece{};
previewPiece.type = static_cast<PieceType>(i); previewPiece.type = static_cast<PieceType>(i);
int maxCy = -1; int maxCy = -1;
@ -600,36 +607,35 @@ void GameRenderer::renderPlayingState(
snprintf(countStr, sizeof(countStr), "%d", count); snprintf(countStr, sizeof(countStr), "%d", count);
int countW = 0, countH = 0; int countW = 0, countH = 0;
pixelFont->measure(countStr, 1.0f, countW, countH); pixelFont->measure(countStr, 1.0f, countW, countH);
float countX = previewX + rowWidth - static_cast<float>(countW); float countX = rowRight - static_cast<float>(countW);
float countY = previewY + 9.0f; float countY = previewY + 4.0f;
int perc = (totalBlocks > 0) ? int(std::round(100.0 * double(count) / double(totalBlocks))) : 0; int perc = (totalBlocks > 0) ? int(std::round(100.0 * double(count) / double(totalBlocks))) : 0;
char percStr[16]; char percStr[16];
snprintf(percStr, sizeof(percStr), "%d%%", perc); snprintf(percStr, sizeof(percStr), "%d%%", perc);
float barX = previewX; float barX = rowLeft + previewSize + 36.0f;
float barY = previewY + pieceHeight + 12.0f; float barY = previewY + pieceHeight + 10.0f;
float barH = 6.0f; float barH = 7.0f;
float barW = rowWidth; float barW = std::max(0.0f, rowRight - barX);
float percY = barY + barH + 8.0f; float percY = barY + barH + 6.0f;
float rowBottom = percY + 16.0f; float rowBottom = percY + 18.0f;
SDL_FRect rowBg{ SDL_FRect rowBg{
previewX - 12.0f, rowLeft - 18.0f,
rowTop - 14.0f, rowTop - 14.0f,
rowWidth + 24.0f, rowWidth + 36.0f,
rowBottom - (rowTop - 14.0f) rowBottom - (rowTop - 14.0f)
}; };
SDL_SetRenderDrawColor(renderer, 18, 26, 40, 200); SDL_SetRenderDrawColor(renderer, 6, 12, 26, 205);
SDL_RenderFillRect(renderer, &rowBg); SDL_RenderFillRect(renderer, &rowBg);
SDL_SetRenderDrawColor(renderer, 70, 100, 150, 210); SDL_SetRenderDrawColor(renderer, 30, 60, 110, 220);
SDL_RenderRect(renderer, &rowBg); SDL_RenderRect(renderer, &rowBg);
drawSmallPiece(renderer, blocksTex, static_cast<PieceType>(i), previewX, previewY, previewSize); drawSmallPiece(renderer, blocksTex, static_cast<PieceType>(i), previewX, previewY, previewSize);
pixelFont->draw(renderer, countX, countY, countStr, 1.0f, {245, 245, 255, 255}); 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_SetRenderDrawColor(renderer, 32, 44, 70, 210);
SDL_FRect track{barX, barY, barW, barH}; SDL_FRect track{barX, barY, barW, barH};
SDL_RenderFillRect(renderer, &track); SDL_RenderFillRect(renderer, &track);
SDL_Color pc = COLORS[i + 1]; SDL_Color pc = COLORS[i + 1];
@ -638,6 +644,11 @@ void GameRenderer::renderPlayingState(
fillW = std::clamp(fillW, 0.0f, barW); fillW = std::clamp(fillW, 0.0f, barW);
SDL_FRect fill{barX, barY, fillW, barH}; SDL_FRect fill{barX, barY, fillW, barH};
SDL_RenderFillRect(renderer, &fill); SDL_RenderFillRect(renderer, &fill);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 45);
SDL_FRect fillHighlight{barX, barY, fillW, barH * 0.35f};
SDL_RenderFillRect(renderer, &fillHighlight);
pixelFont->draw(renderer, barX, percY, percStr, 0.78f, {185, 205, 230, 255});
yCursor = rowBottom + rowSpacing; yCursor = rowBottom + rowSpacing;
} }

View File

@ -247,6 +247,31 @@ static void renderScaledBackground(SDL_Renderer* renderer, SDL_Texture* tex, int
SDL_SetTextureAlphaMod(tex, 255); SDL_SetTextureAlphaMod(tex, 255);
} }
static void renderDynamicBackground(SDL_Renderer* renderer, SDL_Texture* tex, int winW, int winH, float baseScale, float motionClockMs, float alphaMul = 1.0f) {
if (!renderer || !tex) {
return;
}
const float seconds = motionClockMs * 0.001f;
const float wobble = std::max(0.4f, baseScale + std::sin(seconds * 0.07f) * 0.02f + std::sin(seconds * 0.23f) * 0.01f);
const float rotation = std::sin(seconds * 0.035f) * 1.25f;
const float panX = std::sin(seconds * 0.11f) * winW * 0.02f;
const float panY = std::cos(seconds * 0.09f) * winH * 0.015f;
SDL_FRect dest{
(winW - winW * wobble) * 0.5f + panX,
(winH - winH * wobble) * 0.5f + panY,
winW * wobble,
winH * wobble
};
SDL_FPoint center{dest.w * 0.5f, dest.h * 0.5f};
Uint8 alpha = static_cast<Uint8>(std::clamp(alphaMul, 0.0f, 1.0f) * 255.0f);
SDL_SetTextureAlphaMod(tex, alpha);
SDL_RenderTextureRotated(renderer, tex, nullptr, &dest, rotation, &center, SDL_FLIP_NONE);
SDL_SetTextureAlphaMod(tex, 255);
}
static void drawOverlay(SDL_Renderer* renderer, const SDL_FRect& rect, SDL_Color color, Uint8 alpha) { static void drawOverlay(SDL_Renderer* renderer, const SDL_FRect& rect, SDL_Color color, Uint8 alpha) {
if (!renderer || alpha == 0) { if (!renderer || alpha == 0) {
return; return;
@ -257,7 +282,7 @@ static void drawOverlay(SDL_Renderer* renderer, const SDL_FRect& rect, SDL_Color
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
} }
static void renderLevelBackgrounds(const LevelBackgroundFader& fader, SDL_Renderer* renderer, int winW, int winH) { static void renderLevelBackgrounds(const LevelBackgroundFader& fader, SDL_Renderer* renderer, int winW, int winH, float motionClockMs) {
if (!renderer) { if (!renderer) {
return; return;
} }
@ -265,12 +290,13 @@ static void renderLevelBackgrounds(const LevelBackgroundFader& fader, SDL_Render
SDL_FRect fullRect{0.f, 0.f, static_cast<float>(winW), static_cast<float>(winH)}; SDL_FRect fullRect{0.f, 0.f, static_cast<float>(winW), static_cast<float>(winH)};
const float duration = std::max(1.0f, fader.phaseDurationMs); const float duration = std::max(1.0f, fader.phaseDurationMs);
const float progress = (fader.phase == LevelBackgroundPhase::Idle) ? 0.0f : std::clamp(fader.phaseElapsedMs / duration, 0.0f, 1.0f); const float progress = (fader.phase == LevelBackgroundPhase::Idle) ? 0.0f : std::clamp(fader.phaseElapsedMs / duration, 0.0f, 1.0f);
const float seconds = motionClockMs * 0.001f;
switch (fader.phase) { switch (fader.phase) {
case LevelBackgroundPhase::ZoomOut: { case LevelBackgroundPhase::ZoomOut: {
const float scale = 1.0f + progress * 0.15f; const float scale = 1.0f + progress * 0.15f;
if (fader.currentTex) { if (fader.currentTex) {
renderScaledBackground(renderer, fader.currentTex, winW, winH, scale, Uint8((1.0f - progress * 0.4f) * 255.0f)); renderDynamicBackground(renderer, fader.currentTex, winW, winH, scale, motionClockMs, (1.0f - progress * 0.4f));
drawOverlay(renderer, fullRect, SDL_Color{0, 0, 0, 255}, Uint8(progress * 200.0f)); drawOverlay(renderer, fullRect, SDL_Color{0, 0, 0, 255}, Uint8(progress * 200.0f));
} }
break; break;
@ -279,16 +305,18 @@ static void renderLevelBackgrounds(const LevelBackgroundFader& fader, SDL_Render
const float scale = 1.10f - progress * 0.10f; const float scale = 1.10f - progress * 0.10f;
const Uint8 alpha = Uint8((0.4f + progress * 0.6f) * 255.0f); const Uint8 alpha = Uint8((0.4f + progress * 0.6f) * 255.0f);
if (fader.currentTex) { if (fader.currentTex) {
renderScaledBackground(renderer, fader.currentTex, winW, winH, scale, alpha); renderDynamicBackground(renderer, fader.currentTex, winW, winH, scale, motionClockMs, alpha / 255.0f);
} }
break; break;
} }
case LevelBackgroundPhase::Idle: case LevelBackgroundPhase::Idle:
default: default:
if (fader.currentTex) { if (fader.currentTex) {
renderScaledBackground(renderer, fader.currentTex, winW, winH, 1.0f, 255); renderDynamicBackground(renderer, fader.currentTex, winW, winH, 1.02f, motionClockMs, 1.0f);
float pulse = 0.35f + 0.25f * (0.5f + 0.5f * std::sin(seconds * 0.5f));
drawOverlay(renderer, fullRect, SDL_Color{5, 12, 28, 255}, Uint8(pulse * 90.0f));
} else if (fader.nextTex) { } else if (fader.nextTex) {
renderScaledBackground(renderer, fader.nextTex, winW, winH, 1.0f, 255); renderDynamicBackground(renderer, fader.nextTex, winW, winH, 1.02f, motionClockMs, 1.0f);
} else { } else {
drawOverlay(renderer, fullRect, SDL_Color{0, 0, 0, 255}, 255); drawOverlay(renderer, fullRect, SDL_Color{0, 0, 0, 255}, 255);
} }
@ -803,6 +831,7 @@ int main(int, char **)
int gameplayCountdownIndex = 0; int gameplayCountdownIndex = 0;
const double GAMEPLAY_COUNTDOWN_STEP_MS = 400.0; const double GAMEPLAY_COUNTDOWN_STEP_MS = 400.0;
const std::array<const char*, 4> GAMEPLAY_COUNTDOWN_LABELS = { "3", "2", "1", "START" }; const std::array<const char*, 4> GAMEPLAY_COUNTDOWN_LABELS = { "3", "2", "1", "START" };
double gameplayBackgroundClockMs = 0.0;
// Instantiate state manager // Instantiate state manager
StateManager stateMgr(state); StateManager stateMgr(state);
@ -1268,6 +1297,7 @@ int main(int, char **)
// Cap frame time to avoid spiral of death (max 100ms) // Cap frame time to avoid spiral of death (max 100ms)
if (frameMs > 100.0) frameMs = 100.0; if (frameMs > 100.0) frameMs = 100.0;
gameplayBackgroundClockMs += frameMs;
const bool *ks = SDL_GetKeyboardState(nullptr); const bool *ks = SDL_GetKeyboardState(nullptr);
bool left = state == AppState::Playing && ks[SDL_SCANCODE_LEFT]; bool left = state == AppState::Playing && ks[SDL_SCANCODE_LEFT];
bool right = state == AppState::Playing && ks[SDL_SCANCODE_RIGHT]; bool right = state == AppState::Playing && ks[SDL_SCANCODE_RIGHT];
@ -1573,7 +1603,7 @@ int main(int, char **)
if (state == AppState::Playing) { if (state == AppState::Playing) {
int bgLevel = std::clamp(game.level(), 0, 32); int bgLevel = std::clamp(game.level(), 0, 32);
queueLevelBackground(levelBackgrounds, renderer, bgLevel); queueLevelBackground(levelBackgrounds, renderer, bgLevel);
renderLevelBackgrounds(levelBackgrounds, renderer, winW, winH); renderLevelBackgrounds(levelBackgrounds, renderer, winW, winH, static_cast<float>(gameplayBackgroundClockMs));
} else if (state == AppState::Loading) { } else if (state == AppState::Loading) {
// Use 3D starfield for loading screen (full screen) // Use 3D starfield for loading screen (full screen)
starfield3D.draw(renderer); starfield3D.draw(renderer);