fixed next block
This commit is contained in:
@ -156,6 +156,99 @@ void GameRenderer::drawSmallPiece(SDL_Renderer* renderer, SDL_Texture* blocksTex
|
||||
}
|
||||
}
|
||||
|
||||
void GameRenderer::renderNextPanel(
|
||||
SDL_Renderer* renderer,
|
||||
FontAtlas* pixelFont,
|
||||
SDL_Texture* blocksTex,
|
||||
const Game::Piece& nextPiece,
|
||||
float panelX,
|
||||
float panelY,
|
||||
float panelW,
|
||||
float panelH,
|
||||
float tileSize
|
||||
) {
|
||||
if (!renderer || !pixelFont) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SDL_Color gridBorderColor{60, 80, 160, 255}; // matches main grid outline
|
||||
const SDL_Color bayColor{8, 12, 24, 235};
|
||||
const SDL_Color bayOutline{25, 62, 86, 220};
|
||||
const SDL_Color labelColor{255, 220, 0, 255};
|
||||
|
||||
SDL_FRect bayRect{panelX, panelY, panelW, panelH};
|
||||
SDL_SetRenderDrawColor(renderer, bayColor.r, bayColor.g, bayColor.b, bayColor.a);
|
||||
SDL_RenderFillRect(renderer, &bayRect);
|
||||
|
||||
SDL_FRect thinOutline{panelX - 1.0f, panelY - 1.0f, panelW + 2.0f, panelH + 2.0f};
|
||||
auto drawOutlineNoBottom = [&](const SDL_FRect& rect, SDL_Color color) {
|
||||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
const float left = rect.x;
|
||||
const float top = rect.y;
|
||||
const float right = rect.x + rect.w;
|
||||
const float bottom = rect.y + rect.h;
|
||||
SDL_RenderLine(renderer, left, top, right, top); // top edge
|
||||
SDL_RenderLine(renderer, left, top, left, bottom); // left edge
|
||||
SDL_RenderLine(renderer, right, top, right, bottom); // right edge
|
||||
};
|
||||
|
||||
drawOutlineNoBottom(thinOutline, gridBorderColor);
|
||||
drawOutlineNoBottom(bayRect, bayOutline);
|
||||
|
||||
const float labelPad = tileSize * 0.25f;
|
||||
pixelFont->draw(renderer, panelX + labelPad, panelY + labelPad * 0.5f, "NEXT", 0.9f, labelColor);
|
||||
|
||||
if (nextPiece.type >= PIECE_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the occupied bounds of the tetromino within its 4x4 local grid.
|
||||
int minCx = 4;
|
||||
int maxCx = -1;
|
||||
int minCy = 4;
|
||||
int maxCy = -1;
|
||||
for (int cy = 0; cy < 4; ++cy) {
|
||||
for (int cx = 0; cx < 4; ++cx) {
|
||||
if (!Game::cellFilled(nextPiece, cx, cy)) {
|
||||
continue;
|
||||
}
|
||||
minCx = std::min(minCx, cx);
|
||||
maxCx = std::max(maxCx, cx);
|
||||
minCy = std::min(minCy, cy);
|
||||
maxCy = std::max(maxCy, cy);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxCx < minCx || maxCy < minCy) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reserve a little headroom for the NEXT label, then center the piece in screen-space.
|
||||
const float labelReserve = tileSize * 0.9f;
|
||||
const float previewTop = panelY + std::min(labelReserve, panelH * 0.45f);
|
||||
const float previewBottom = panelY + panelH - tileSize * 0.25f;
|
||||
const float previewCenterY = (previewTop + previewBottom) * 0.5f;
|
||||
const float previewCenterX = panelX + panelW * 0.5f;
|
||||
|
||||
const float pieceWidth = static_cast<float>(maxCx - minCx + 1) * tileSize;
|
||||
const float pieceHeight = static_cast<float>(maxCy - minCy + 1) * tileSize;
|
||||
// Nudge preview slightly to the right so it aligns with the main grid's visual columns
|
||||
const float previewNudgeX = tileSize * 0.5f;
|
||||
const float startX = previewCenterX - pieceWidth * 0.5f - static_cast<float>(minCx) * tileSize + previewNudgeX;
|
||||
const float startY = previewCenterY - pieceHeight * 0.5f - static_cast<float>(minCy) * tileSize;
|
||||
|
||||
for (int cy = 0; cy < 4; ++cy) {
|
||||
for (int cx = 0; cx < 4; ++cx) {
|
||||
if (!Game::cellFilled(nextPiece, cx, cy)) {
|
||||
continue;
|
||||
}
|
||||
const float px = startX + static_cast<float>(cx) * tileSize;
|
||||
const float py = startY + static_cast<float>(cy) * tileSize;
|
||||
drawBlockTexture(renderer, blocksTex, px, py, tileSize, nextPiece.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameRenderer::renderPlayingState(
|
||||
SDL_Renderer* renderer,
|
||||
Game* game,
|
||||
@ -235,10 +328,10 @@ void GameRenderer::renderPlayingState(
|
||||
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;
|
||||
const float NEXT_PANEL_WIDTH = finalBlockSize * 6.0f; // +1 cell padding on each horizontal side
|
||||
const float NEXT_PANEL_HEIGHT = finalBlockSize * 3.0f;
|
||||
const float NEXT_PANEL_X = gridX + (GRID_W - NEXT_PANEL_WIDTH) * 0.5f;
|
||||
const float NEXT_PANEL_Y = gridY - NEXT_PANEL_HEIGHT - 2.0f; // nudge up ~2px
|
||||
|
||||
// Handle line clearing effects
|
||||
if (game->hasCompletedLines() && lineEffect && !lineEffect->isActive()) {
|
||||
@ -248,7 +341,17 @@ void GameRenderer::renderPlayingState(
|
||||
|
||||
// Draw game grid border
|
||||
drawRectWithOffset(gridX - 3 - contentOffsetX, gridY - 3 - contentOffsetY, GRID_W + 6, GRID_H + 6, {100, 120, 200, 255});
|
||||
drawRectWithOffset(gridX - 1 - contentOffsetX, gridY - 1 - contentOffsetY, GRID_W + 2, GRID_H + 2, {60, 80, 160, 255});
|
||||
// Draw a 1px blue border around grid but omit the top horizontal so the NEXT panel
|
||||
// can visually join seamlessly. We'll draw left, right and bottom bands manually.
|
||||
{
|
||||
SDL_Color blue{60, 80, 160, 255};
|
||||
// left vertical band (1px wide)
|
||||
drawRectWithOffset(gridX - 1 - contentOffsetX, gridY - contentOffsetY, 1.0f, GRID_H, blue);
|
||||
// right vertical band (1px wide)
|
||||
drawRectWithOffset(gridX + GRID_W - contentOffsetX, gridY - contentOffsetY, 1.0f, GRID_H, blue);
|
||||
// bottom horizontal band (1px high)
|
||||
drawRectWithOffset(gridX - 1 - contentOffsetX, gridY + GRID_H - contentOffsetY, GRID_W + 2.0f, 1.0f, blue);
|
||||
}
|
||||
drawRectWithOffset(gridX - contentOffsetX, gridY - contentOffsetY, GRID_W, GRID_H, {20, 25, 35, 255});
|
||||
|
||||
// Draw stats panel backdrop using the same art as the score panel
|
||||
@ -442,10 +545,16 @@ void GameRenderer::renderPlayingState(
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawBlendMode(renderer, oldBlend);
|
||||
|
||||
// Draw next piece preview panel border
|
||||
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});
|
||||
|
||||
renderNextPanel(renderer, pixelFont, blocksTex, game->next(), NEXT_PANEL_X, NEXT_PANEL_Y, NEXT_PANEL_WIDTH, NEXT_PANEL_HEIGHT, finalBlockSize);
|
||||
|
||||
// Draw a thin horizontal connector at the bottom of the NEXT panel so it visually
|
||||
// connects to the top of the grid (appears as a single continuous frame).
|
||||
SDL_SetRenderDrawColor(renderer, 60, 80, 160, 255); // same as grid border
|
||||
float connectorY = NEXT_PANEL_Y + NEXT_PANEL_HEIGHT; // bottom of next panel (near grid top)
|
||||
// Draw a 2px-high filled connector to overwrite any existing grid border pixels
|
||||
SDL_FRect connRect{ NEXT_PANEL_X, connectorY - 1.0f, NEXT_PANEL_WIDTH, 2.0f };
|
||||
SDL_RenderFillRect(renderer, &connRect);
|
||||
|
||||
// Precompute row drop offsets (line collapse effect)
|
||||
std::array<float, Game::ROWS> rowDropOffsets{};
|
||||
@ -695,12 +804,6 @@ void GameRenderer::renderPlayingState(
|
||||
lineEffect->render(renderer, blocksTex, static_cast<int>(gridX), static_cast<int>(gridY), static_cast<int>(finalBlockSize));
|
||||
}
|
||||
|
||||
// Draw next piece preview
|
||||
pixelFont->draw(renderer, nextX + 10, nextY - 20, "NEXT", 1.0f, {255, 220, 0, 255});
|
||||
if (game->next().type < PIECE_COUNT) {
|
||||
drawSmallPiece(renderer, blocksTex, static_cast<PieceType>(game->next().type), nextX + 10, nextY + 10, finalBlockSize * 0.6f);
|
||||
}
|
||||
|
||||
// Draw block statistics (left panel)
|
||||
const auto& blockCounts = game->getBlockCounts();
|
||||
int totalBlocks = 0;
|
||||
|
||||
Reference in New Issue
Block a user