fixed I block in coop mode

This commit is contained in:
2025-12-21 18:11:21 +01:00
parent 744268fedd
commit 33d5eedec8

View File

@ -2282,22 +2282,10 @@ void GameRenderer::renderCoopPlayingState(
sf.seq = seq; sf.seq = seq;
sf.piece = game->current(side); sf.piece = game->current(side);
sf.tileSize = finalBlockSize; sf.tileSize = finalBlockSize;
// Target to first visible row (row 0) // Note: targetX/targetY are recomputed during drawing using the live
// current piece so movement/rotation during the fade stays correct.
sf.targetX = gridX + static_cast<float>(sf.piece.x) * finalBlockSize; sf.targetX = gridX + static_cast<float>(sf.piece.x) * finalBlockSize;
// IMPORTANT: In classic mode, pieces can spawn with their first filled sf.targetY = gridY;
// cell not at cy=0 within the 4x4. To avoid appearing one row too low
// (and then jumping up), align the topmost filled cell to row 0.
int minCy = 4;
for (int cy = 0; cy < 4; ++cy) {
for (int cx = 0; cx < 4; ++cx) {
if (!CoopGame::cellFilled(sf.piece, cx, cy)) continue;
minCy = std::min(minCy, cy);
}
}
if (minCy == 4) {
minCy = 0;
}
sf.targetY = gridY - static_cast<float>(minCy) * finalBlockSize;
} else { } else {
// Reuse exact horizontal smoothing from single-player // Reuse exact horizontal smoothing from single-player
constexpr float HORIZONTAL_SMOOTH_MS = 55.0f; constexpr float HORIZONTAL_SMOOTH_MS = 55.0f;
@ -2373,24 +2361,54 @@ void GameRenderer::renderCoopPlayingState(
// Draw any active spawn fades (alpha ramp into first row). Draw before // Draw any active spawn fades (alpha ramp into first row). Draw before
// the regular active pieces; while the spawn fade is active the piece's // the regular active pieces; while the spawn fade is active the piece's
// real position is above the grid and will not be drawn by drawPiece. // real position is above the grid and will not be drawn by drawPiece.
auto drawSpawnFadeIfActive = [&](SpawnFadeState &sf) { auto drawSpawnFadeIfActive = [&](SpawnFadeState &sf, CoopGame::PlayerSide side) {
if (!sf.active) return; if (!sf.active) return;
Uint32 now = SDL_GetTicks();
float elapsed = static_cast<float>(now - sf.startTick); // If the piece has already changed, stop the fade.
const uint64_t currentSeq = game->currentPieceSequence(side);
if (sf.seq != currentSeq) {
sf.active = false;
return;
}
const CoopGame::Piece& livePiece = game->current(side);
float elapsed = static_cast<float>(nowTicks - sf.startTick);
float t = sf.durationMs <= 0.0f ? 1.0f : std::clamp(elapsed / sf.durationMs, 0.0f, 1.0f); float t = sf.durationMs <= 0.0f ? 1.0f : std::clamp(elapsed / sf.durationMs, 0.0f, 1.0f);
Uint8 alpha = static_cast<Uint8>(std::lround(255.0f * t)); Uint8 alpha = static_cast<Uint8>(std::lround(255.0f * t));
if (blocksTex) SDL_SetTextureAlphaMod(blocksTex, alpha); if (blocksTex) SDL_SetTextureAlphaMod(blocksTex, alpha);
// Draw piece at target (first row)
// Align the topmost filled cell to row 0 (first visible row), like classic.
int minCy = 4;
for (int cy = 0; cy < 4; ++cy) { for (int cy = 0; cy < 4; ++cy) {
for (int cx = 0; cx < 4; ++cx) { for (int cx = 0; cx < 4; ++cx) {
if (!CoopGame::cellFilled(sf.piece, cx, cy)) continue; if (!CoopGame::cellFilled(livePiece, cx, cy)) continue;
minCy = std::min(minCy, cy);
}
}
if (minCy == 4) {
minCy = 0;
}
sf.targetX = gridX + static_cast<float>(livePiece.x) * sf.tileSize;
sf.targetY = gridY - static_cast<float>(minCy) * sf.tileSize;
// Draw the live piece at the fade target.
for (int cy = 0; cy < 4; ++cy) {
for (int cx = 0; cx < 4; ++cx) {
if (!CoopGame::cellFilled(livePiece, cx, cy)) continue;
float px = sf.targetX + static_cast<float>(cx) * sf.tileSize; float px = sf.targetX + static_cast<float>(cx) * sf.tileSize;
float py = sf.targetY + static_cast<float>(cy) * sf.tileSize; float py = sf.targetY + static_cast<float>(cy) * sf.tileSize;
drawBlockTexturePublic(renderer, blocksTex, px, py, sf.tileSize, sf.piece.type); drawBlockTexturePublic(renderer, blocksTex, px, py, sf.tileSize, livePiece.type);
} }
} }
if (blocksTex) SDL_SetTextureAlphaMod(blocksTex, 255); if (blocksTex) SDL_SetTextureAlphaMod(blocksTex, 255);
if (t >= 1.0f) sf.active = false;
// Critical: only deactivate once the real piece is actually drawable.
// Otherwise (notably for I spawning at y=-2) there can be a brief gap where
// the fade ends but the real piece is still fully above the visible grid.
const bool pieceHasAnyVisibleCell = (livePiece.y + minCy) >= 0;
if (t >= 1.0f && pieceHasAnyVisibleCell) {
sf.active = false;
}
}; };
auto drawPiece = [&](const CoopGame::Piece& p, const std::pair<float, float>& offsets, bool isGhost) { auto drawPiece = [&](const CoopGame::Piece& p, const std::pair<float, float>& offsets, bool isGhost) {
@ -2419,8 +2437,8 @@ void GameRenderer::renderCoopPlayingState(
const auto leftOffsets = computeOffsets(CoopGame::PlayerSide::Left, s_leftSmooth); const auto leftOffsets = computeOffsets(CoopGame::PlayerSide::Left, s_leftSmooth);
const auto rightOffsets = computeOffsets(CoopGame::PlayerSide::Right, s_rightSmooth); const auto rightOffsets = computeOffsets(CoopGame::PlayerSide::Right, s_rightSmooth);
// Draw transient spawn fades (if active) into the first visible row // Draw transient spawn fades (if active) into the first visible row
drawSpawnFadeIfActive(s_leftSpawnFade); drawSpawnFadeIfActive(s_leftSpawnFade, CoopGame::PlayerSide::Left);
drawSpawnFadeIfActive(s_rightSpawnFade); drawSpawnFadeIfActive(s_rightSpawnFade, CoopGame::PlayerSide::Right);
// Draw classic-style ghost pieces (landing position), grid-aligned. // Draw classic-style ghost pieces (landing position), grid-aligned.
// This intentionally does NOT use smoothing offsets. // This intentionally does NOT use smoothing offsets.