From 4efb60bb5be6545f024a50bb2ccdbcc7876a08c4 Mon Sep 17 00:00:00 2001 From: Gregor Klevze Date: Sun, 21 Dec 2025 15:59:46 +0100 Subject: [PATCH] added ghost block --- src/graphics/renderers/GameRenderer.cpp | 50 +++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/graphics/renderers/GameRenderer.cpp b/src/graphics/renderers/GameRenderer.cpp index 0cc1dab..57d9dbe 100644 --- a/src/graphics/renderers/GameRenderer.cpp +++ b/src/graphics/renderers/GameRenderer.cpp @@ -2106,7 +2106,7 @@ void GameRenderer::renderCoopPlayingState( if (t >= 1.0f) sf.active = false; }; - auto drawPiece = [&](const CoopGame::Piece& p, CoopGame::PlayerSide side, const std::pair& offsets) { + auto drawPiece = [&](const CoopGame::Piece& p, const std::pair& offsets, bool isGhost) { for (int cy = 0; cy < 4; ++cy) { for (int cx = 0; cx < 4; ++cx) { if (!CoopGame::cellFilled(p, cx, cy)) continue; @@ -2115,7 +2115,17 @@ void GameRenderer::renderCoopPlayingState( if (pyIdx < 0) continue; // don't draw parts above the visible grid float px = gridX + (float)pxIdx * finalBlockSize + offsets.first; float py = gridY + (float)pyIdx * finalBlockSize + offsets.second; - drawBlockTexturePublic(renderer, blocksTex, px, py, finalBlockSize, p.type); + if (isGhost) { + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(renderer, 180, 180, 180, 20); + SDL_FRect rect = {px + 2.0f, py + 2.0f, finalBlockSize - 4.0f, finalBlockSize - 4.0f}; + SDL_RenderFillRect(renderer, &rect); + SDL_SetRenderDrawColor(renderer, 180, 180, 180, 30); + SDL_FRect border = {px + 1.0f, py + 1.0f, finalBlockSize - 2.0f, finalBlockSize - 2.0f}; + SDL_RenderRect(renderer, &border); + } else { + drawBlockTexturePublic(renderer, blocksTex, px, py, finalBlockSize, p.type); + } } } }; @@ -2125,14 +2135,46 @@ void GameRenderer::renderCoopPlayingState( drawSpawnFadeIfActive(s_leftSpawnFade); drawSpawnFadeIfActive(s_rightSpawnFade); + // Draw classic-style ghost pieces (landing position), grid-aligned. + // This intentionally does NOT use smoothing offsets. + auto computeGhostPiece = [&](CoopGame::PlayerSide side) { + CoopGame::Piece ghostPiece = game->current(side); + const auto& boardRef = game->boardRef(); + while (true) { + CoopGame::Piece testPiece = ghostPiece; + testPiece.y++; + bool collision = false; + for (int cy = 0; cy < 4; ++cy) { + for (int cx = 0; cx < 4; ++cx) { + if (!CoopGame::cellFilled(testPiece, cx, cy)) continue; + int gx = testPiece.x + cx; + int gy = testPiece.y + cy; + if (gy >= CoopGame::ROWS || gx < 0 || gx >= CoopGame::COLS || + (gy >= 0 && boardRef[gy * CoopGame::COLS + gx].occupied)) { + collision = true; + break; + } + } + if (collision) break; + } + if (collision) break; + ghostPiece = testPiece; + } + return ghostPiece; + }; + + const std::pair ghostOffsets{0.0f, 0.0f}; + drawPiece(computeGhostPiece(CoopGame::PlayerSide::Left), ghostOffsets, true); + drawPiece(computeGhostPiece(CoopGame::PlayerSide::Right), ghostOffsets, true); + // If a spawn fade is active for a side and matches the current piece // sequence, only draw the fade visual and skip the regular piece draw // to avoid a double-draw that appears as a jump when falling starts. if (!(s_leftSpawnFade.active && s_leftSpawnFade.seq == game->currentPieceSequence(CoopGame::PlayerSide::Left))) { - drawPiece(game->current(CoopGame::PlayerSide::Left), CoopGame::PlayerSide::Left, leftOffsets); + drawPiece(game->current(CoopGame::PlayerSide::Left), leftOffsets, false); } if (!(s_rightSpawnFade.active && s_rightSpawnFade.seq == game->currentPieceSequence(CoopGame::PlayerSide::Right))) { - drawPiece(game->current(CoopGame::PlayerSide::Right), CoopGame::PlayerSide::Right, rightOffsets); + drawPiece(game->current(CoopGame::PlayerSide::Right), rightOffsets, false); } // Next panels (two)