diff --git a/assets/images/next_panel.png b/assets/images/next_panel.png new file mode 100644 index 0000000..d57947a Binary files /dev/null and b/assets/images/next_panel.png differ diff --git a/src/core/application/ApplicationManager.cpp b/src/core/application/ApplicationManager.cpp index ace8ebc..f626e04 100644 --- a/src/core/application/ApplicationManager.cpp +++ b/src/core/application/ApplicationManager.cpp @@ -1133,6 +1133,7 @@ void ApplicationManager::setupStateHandlers() { m_stateContext.blocksTex, m_stateContext.statisticsPanelTex, m_stateContext.scorePanelTex, + m_stateContext.nextPanelTex, LOGICAL_W, LOGICAL_H, logicalScale, diff --git a/src/graphics/GameRenderer.cpp b/src/graphics/GameRenderer.cpp index c1f7652..4a5f339 100644 --- a/src/graphics/GameRenderer.cpp +++ b/src/graphics/GameRenderer.cpp @@ -122,6 +122,9 @@ void GameRenderer::renderPlayingState( FontAtlas* pixelFont, LineEffect* lineEffect, SDL_Texture* blocksTex, + SDL_Texture* statisticsPanelTex, + SDL_Texture* scorePanelTex, + SDL_Texture* nextPanelTex, float logicalW, float logicalH, float logicalScale, @@ -242,8 +245,16 @@ void GameRenderer::renderPlayingState( drawRectWithOffset(statsX - contentOffsetX, statsY - contentOffsetY, statsW, statsH, {30, 35, 50, 255}); // 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}); + // If a NEXT panel texture was provided, draw it instead of the custom + // background/outline. The texture will be scaled to fit the panel area. + if (nextPanelTex) { + SDL_FRect dst{ nextX - contentOffsetX, nextY - contentOffsetY, nextW, nextH }; + SDL_SetTextureBlendMode(nextPanelTex, SDL_BLENDMODE_BLEND); + SDL_RenderTexture(renderer, nextPanelTex, nullptr, &dst); + } else { + 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}); + } // Draw the game board const auto &board = game->boardRef(); @@ -306,7 +317,8 @@ void GameRenderer::renderPlayingState( // 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(game->next().type), nextX + 10, nextY + 10, finalBlockSize * 0.6f); + // Nudge preview slightly upward to remove thin bottom artifact + drawSmallPiece(renderer, blocksTex, static_cast(game->next().type), nextX + 10, nextY + 5, finalBlockSize * 0.6f); } // Draw block statistics (left panel) diff --git a/src/graphics/GameRenderer.h b/src/graphics/GameRenderer.h index 4e7767e..5b074da 100644 --- a/src/graphics/GameRenderer.h +++ b/src/graphics/GameRenderer.h @@ -20,8 +20,11 @@ public: Game* game, FontAtlas* pixelFont, LineEffect* lineEffect, - SDL_Texture* blocksTex, - float logicalW, + SDL_Texture* blocksTex, + SDL_Texture* statisticsPanelTex, + SDL_Texture* scorePanelTex, + SDL_Texture* nextPanelTex, + float logicalW, float logicalH, float logicalScale, float winW, diff --git a/src/graphics/renderers/GameRenderer.cpp b/src/graphics/renderers/GameRenderer.cpp index 8cd61fb..85db0ba 100644 --- a/src/graphics/renderers/GameRenderer.cpp +++ b/src/graphics/renderers/GameRenderer.cpp @@ -152,7 +152,8 @@ void GameRenderer::startTransportEffectForGame(Game* game, SDL_Texture* blocksTe float rel = startX - gridOriginX; float nearestTile = std::round(rel / finalBlockSize); startX = gridOriginX + nearestTile * finalBlockSize; - startY = std::round(startY); + // Raise preview slightly to remove a thin line artifact under the panel + startY = std::round(startY) - 5.0f; // Target is the current piece's grid position float targetX = gridX + piece.x * finalBlockSize; @@ -180,7 +181,7 @@ void GameRenderer::startTransportEffectForGame(Game* game, SDL_Texture* blocksTe float rel2 = nextPreviewX - gridOriginX2; float nearestTile2 = std::round(rel2 / finalBlockSize); nextPreviewX = gridOriginX2 + nearestTile2 * finalBlockSize; - nextPreviewY = std::round(nextPreviewY); + nextPreviewY = std::round(nextPreviewY) - 5.0f; // Initialize transport state to perform fades: preview fade-out -> grid fade-in -> next preview fade-in s_transport.active = true; @@ -396,6 +397,7 @@ void GameRenderer::renderNextPanel( SDL_Renderer* renderer, FontAtlas* pixelFont, SDL_Texture* blocksTex, + SDL_Texture* nextPanelTex, const Game::Piece& nextPiece, float panelX, float panelY, @@ -412,27 +414,37 @@ void GameRenderer::renderNextPanel( 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); + // If an external NEXT panel texture is provided, draw it scaled into + // the panel rectangle and skip the custom background/frame drawing. + if (nextPanelTex) { + SDL_FRect dst{panelX, panelY, panelW, panelH}; + SDL_RenderTexture(renderer, nextPanelTex, nullptr, &dst); + // Draw the panel label over the texture — user requested visible label + const float labelPad = tileSize * 0.25f; + pixelFont->draw(renderer, panelX + labelPad, panelY + labelPad * 0.5f, "NEXT", 0.9f, labelColor); + } else { + 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 - }; + 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); + //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); + 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; @@ -478,8 +490,8 @@ void GameRenderer::renderNextPanel( float rel = startX - gridOriginX; float nearestTile = std::round(rel / tileSize); startX = gridOriginX + nearestTile * tileSize; - // Round Y to pixel to avoid subpixel artifacts - startY = std::round(startY); + // Round Y to pixel to avoid subpixel artifacts and nudge upward slightly + startY = std::round(startY) - 5.0f; // If a transfer fade is active, the preview cells will be drawn by the // transport effect (with fade). Skip drawing the normal preview in that case. @@ -505,6 +517,7 @@ void GameRenderer::renderPlayingState( SDL_Texture* blocksTex, SDL_Texture* statisticsPanelTex, SDL_Texture* scorePanelTex, + SDL_Texture* nextPanelTex, float logicalW, float logicalH, float logicalScale, @@ -821,7 +834,7 @@ void GameRenderer::renderPlayingState( SDL_SetRenderDrawBlendMode(renderer, oldBlend); - renderNextPanel(renderer, pixelFont, blocksTex, game->next(), NEXT_PANEL_X, NEXT_PANEL_Y, NEXT_PANEL_WIDTH, NEXT_PANEL_HEIGHT, finalBlockSize); + renderNextPanel(renderer, pixelFont, blocksTex, nextPanelTex, game->next(), NEXT_PANEL_X, NEXT_PANEL_Y, NEXT_PANEL_WIDTH, NEXT_PANEL_HEIGHT, finalBlockSize); // Draw a small filled connector to visually merge NEXT panel and grid border SDL_SetRenderDrawColor(renderer, 60, 80, 160, 255); // same as grid border diff --git a/src/graphics/renderers/GameRenderer.h b/src/graphics/renderers/GameRenderer.h index 33b4654..14fd746 100644 --- a/src/graphics/renderers/GameRenderer.h +++ b/src/graphics/renderers/GameRenderer.h @@ -23,6 +23,7 @@ public: SDL_Texture* blocksTex, SDL_Texture* statisticsPanelTex, SDL_Texture* scorePanelTex, + SDL_Texture* nextPanelTex, float logicalW, float logicalH, float logicalScale, @@ -69,7 +70,7 @@ private: static void drawBlockTexture(SDL_Renderer* renderer, SDL_Texture* blocksTex, float x, float y, float size, int blockType); static void drawPiece(SDL_Renderer* renderer, SDL_Texture* blocksTex, const Game::Piece& piece, float ox, float oy, float tileSize, bool isGhost = false, float pixelOffsetX = 0.0f, float pixelOffsetY = 0.0f); static void drawSmallPiece(SDL_Renderer* renderer, SDL_Texture* blocksTex, PieceType pieceType, float x, float y, float tileSize); - static void renderNextPanel(SDL_Renderer* renderer, FontAtlas* pixelFont, SDL_Texture* blocksTex, const Game::Piece& nextPiece, float panelX, float panelY, float panelW, float panelH, float tileSize); + static void renderNextPanel(SDL_Renderer* renderer, FontAtlas* pixelFont, SDL_Texture* blocksTex, SDL_Texture* nextPanelTex, const Game::Piece& nextPiece, float panelX, float panelY, float panelW, float panelH, float tileSize); // Helper function for drawing rectangles static void drawRect(SDL_Renderer* renderer, float x, float y, float w, float h, SDL_Color c); diff --git a/src/main.cpp b/src/main.cpp index bda5a62..2c7e876 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -710,6 +710,10 @@ int main(int, char **) if (statisticsPanelTex) { SDL_SetTextureBlendMode(statisticsPanelTex, SDL_BLENDMODE_BLEND); } + SDL_Texture* nextPanelTex = loadTextureFromImage(renderer, "assets/images/next_panel.png"); + if (nextPanelTex) { + SDL_SetTextureBlendMode(nextPanelTex, SDL_BLENDMODE_BLEND); + } Game game(startLevelSelection); // Apply global gravity speed multiplier from config @@ -859,6 +863,7 @@ int main(int, char **) ctx.blocksTex = blocksTex; ctx.scorePanelTex = scorePanelTex; ctx.statisticsPanelTex = statisticsPanelTex; + ctx.nextPanelTex = nextPanelTex; ctx.mainScreenTex = mainScreenTex; ctx.mainScreenW = mainScreenW; ctx.mainScreenH = mainScreenH; @@ -1760,6 +1765,7 @@ int main(int, char **) blocksTex, ctx.statisticsPanelTex, scorePanelTex, + nextPanelTex, (float)LOGICAL_W, (float)LOGICAL_H, logicalScale, diff --git a/src/states/PlayingState.cpp b/src/states/PlayingState.cpp index 35a59f6..4e846b1 100644 --- a/src/states/PlayingState.cpp +++ b/src/states/PlayingState.cpp @@ -223,6 +223,7 @@ void PlayingState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect l ctx.blocksTex, ctx.statisticsPanelTex, ctx.scorePanelTex, + ctx.nextPanelTex, 1200.0f, // LOGICAL_W 1000.0f, // LOGICAL_H logicalScale, @@ -309,6 +310,7 @@ void PlayingState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect l ctx.blocksTex, ctx.statisticsPanelTex, ctx.scorePanelTex, + ctx.nextPanelTex, 1200.0f, 1000.0f, logicalScale, diff --git a/src/states/State.h b/src/states/State.h index cc053c8..6aa99e6 100644 --- a/src/states/State.h +++ b/src/states/State.h @@ -42,6 +42,7 @@ struct StateContext { SDL_Texture* blocksTex = nullptr; SDL_Texture* scorePanelTex = nullptr; SDL_Texture* statisticsPanelTex = nullptr; + SDL_Texture* nextPanelTex = nullptr; SDL_Texture* mainScreenTex = nullptr; int mainScreenW = 0; int mainScreenH = 0;