basic gameplay for cooperative
This commit is contained in:
@ -1,5 +1,7 @@
|
||||
#include "GameRenderer.h"
|
||||
#include "../../gameplay/core/Game.h"
|
||||
#include "../../gameplay/coop/CoopGame.h"
|
||||
#include "../../app/Fireworks.h"
|
||||
#include "../ui/Font.h"
|
||||
#include "../../gameplay/effects/LineEffect.h"
|
||||
#include <algorithm>
|
||||
@ -693,6 +695,11 @@ void GameRenderer::renderPlayingState(
|
||||
if (game->hasCompletedLines() && lineEffect && !lineEffect->isActive()) {
|
||||
auto completedLines = game->getCompletedLines();
|
||||
lineEffect->startLineClear(completedLines, static_cast<int>(gridX), static_cast<int>(gridY), static_cast<int>(finalBlockSize));
|
||||
// Trigger fireworks visually for a 4-line clear (TETRIS)
|
||||
if (completedLines.size() == 4) {
|
||||
// spawn near center of grid
|
||||
AppFireworks::spawn(gridX + GRID_W * 0.5f, gridY + GRID_H * 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw game grid border
|
||||
@ -1356,6 +1363,24 @@ void GameRenderer::renderPlayingState(
|
||||
activePiecePixelOffsetY = std::min(activePiecePixelOffsetY, maxAllowed);
|
||||
}
|
||||
|
||||
// Debug: log single-player smoothing/fall values when enabled
|
||||
if (Settings::instance().isDebugEnabled()) {
|
||||
float sp_targetX = static_cast<float>(game->current().x);
|
||||
double sp_gravityMs = game->getGravityMs();
|
||||
double sp_fallAcc = game->getFallAccumulator();
|
||||
int sp_soft = game->isSoftDropping() ? 1 : 0;
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SP OFFSETS: seq=%llu visX=%.3f targX=%.3f offX=%.2f offY=%.2f gravMs=%.2f fallAcc=%.2f soft=%d",
|
||||
(unsigned long long)s_activePieceSmooth.sequence,
|
||||
s_activePieceSmooth.visualX,
|
||||
sp_targetX,
|
||||
activePiecePixelOffsetX,
|
||||
activePiecePixelOffsetY,
|
||||
sp_gravityMs,
|
||||
sp_fallAcc,
|
||||
sp_soft
|
||||
);
|
||||
}
|
||||
|
||||
// Draw ghost piece (where current piece will land)
|
||||
if (allowActivePieceRender) {
|
||||
Game::Piece ghostPiece = game->current();
|
||||
@ -1806,6 +1831,362 @@ void GameRenderer::renderPlayingState(
|
||||
// Exit popup logic moved to renderExitPopup
|
||||
}
|
||||
|
||||
void GameRenderer::renderCoopPlayingState(
|
||||
SDL_Renderer* renderer,
|
||||
CoopGame* game,
|
||||
FontAtlas* pixelFont,
|
||||
LineEffect* lineEffect,
|
||||
SDL_Texture* blocksTex,
|
||||
SDL_Texture* statisticsPanelTex,
|
||||
SDL_Texture* scorePanelTex,
|
||||
SDL_Texture* nextPanelTex,
|
||||
SDL_Texture* holdPanelTex,
|
||||
float logicalW,
|
||||
float logicalH,
|
||||
float logicalScale,
|
||||
float winW,
|
||||
float winH
|
||||
) {
|
||||
if (!renderer || !game || !pixelFont) return;
|
||||
|
||||
static Uint32 s_lastCoopTick = SDL_GetTicks();
|
||||
Uint32 nowTicks = SDL_GetTicks();
|
||||
float deltaMs = static_cast<float>(nowTicks - s_lastCoopTick);
|
||||
s_lastCoopTick = nowTicks;
|
||||
if (deltaMs < 0.0f || deltaMs > 100.0f) {
|
||||
deltaMs = 16.0f;
|
||||
}
|
||||
|
||||
const bool smoothScrollEnabled = Settings::instance().isSmoothScrollEnabled();
|
||||
struct SmoothState { bool initialized{false}; uint64_t seq{0}; float visualX{0.0f}; float visualY{0.0f}; };
|
||||
static SmoothState s_leftSmooth{};
|
||||
static SmoothState s_rightSmooth{};
|
||||
struct SpawnFadeState { bool active{false}; uint64_t seq{0}; Uint32 startTick{0}; float durationMs{200.0f}; CoopGame::Piece piece; float targetX{0.0f}; float targetY{0.0f}; float tileSize{0.0f}; };
|
||||
static SpawnFadeState s_leftSpawnFade{};
|
||||
static SpawnFadeState s_rightSpawnFade{};
|
||||
|
||||
// Layout constants (reuse single-player feel but sized for 20 cols)
|
||||
const float MIN_MARGIN = 40.0f;
|
||||
const float TOP_MARGIN = 60.0f;
|
||||
const float PANEL_WIDTH = 180.0f;
|
||||
const float PANEL_SPACING = 30.0f;
|
||||
const float NEXT_PANEL_HEIGHT = 120.0f;
|
||||
const float BOTTOM_MARGIN = 60.0f;
|
||||
|
||||
// Content offset (centered logical viewport inside window)
|
||||
float contentScale = logicalScale;
|
||||
float contentW = logicalW * contentScale;
|
||||
float contentH = logicalH * contentScale;
|
||||
float contentOffsetX = (winW - contentW) * 0.5f / contentScale;
|
||||
float contentOffsetY = (winH - contentH) * 0.5f / contentScale;
|
||||
|
||||
auto drawRectWithOffset = [&](float x, float y, float w, float h, SDL_Color c) {
|
||||
SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a);
|
||||
SDL_FRect fr{x + contentOffsetX, y + contentOffsetY, w, h};
|
||||
SDL_RenderFillRect(renderer, &fr);
|
||||
};
|
||||
|
||||
const float availableWidth = logicalW - (MIN_MARGIN * 2) - (PANEL_WIDTH * 2) - (PANEL_SPACING * 2);
|
||||
const float availableHeight = logicalH - TOP_MARGIN - BOTTOM_MARGIN - NEXT_PANEL_HEIGHT;
|
||||
|
||||
const float maxBlockSizeW = availableWidth / CoopGame::COLS;
|
||||
const float maxBlockSizeH = availableHeight / CoopGame::ROWS;
|
||||
const float BLOCK_SIZE = std::min(maxBlockSizeW, maxBlockSizeH);
|
||||
const float finalBlockSize = std::max(16.0f, std::min(BLOCK_SIZE, 36.0f));
|
||||
|
||||
const float GRID_W = CoopGame::COLS * finalBlockSize;
|
||||
const float GRID_H = CoopGame::ROWS * finalBlockSize;
|
||||
|
||||
const float totalContentHeight = NEXT_PANEL_HEIGHT + GRID_H;
|
||||
const float availableVerticalSpace = logicalH - TOP_MARGIN - BOTTOM_MARGIN;
|
||||
const float verticalCenterOffset = (availableVerticalSpace - totalContentHeight) * 0.5f;
|
||||
const float contentStartY = TOP_MARGIN + verticalCenterOffset;
|
||||
|
||||
const float totalLayoutWidth = PANEL_WIDTH + PANEL_SPACING + GRID_W + PANEL_SPACING + PANEL_WIDTH;
|
||||
const float layoutStartX = (logicalW - totalLayoutWidth) * 0.5f;
|
||||
|
||||
const float statsX = layoutStartX + contentOffsetX;
|
||||
const float gridX = layoutStartX + PANEL_WIDTH + PANEL_SPACING + contentOffsetX;
|
||||
const float gridY = contentStartY + NEXT_PANEL_HEIGHT + contentOffsetY;
|
||||
|
||||
const float statsY = gridY;
|
||||
const float statsW = PANEL_WIDTH;
|
||||
const float statsH = GRID_H;
|
||||
|
||||
// Shared score panel (reuse existing art)
|
||||
SDL_FRect scorePanelBg{ statsX - 20.0f, gridY - 26.0f, statsW + 40.0f, GRID_H + 52.0f };
|
||||
if (statisticsPanelTex) {
|
||||
SDL_RenderTexture(renderer, statisticsPanelTex, nullptr, &scorePanelBg);
|
||||
} else if (scorePanelTex) {
|
||||
SDL_RenderTexture(renderer, scorePanelTex, nullptr, &scorePanelBg);
|
||||
} else {
|
||||
drawRectWithOffset(scorePanelBg.x - contentOffsetX, scorePanelBg.y - contentOffsetY, scorePanelBg.w, scorePanelBg.h, SDL_Color{12,18,32,205});
|
||||
}
|
||||
|
||||
// Handle line clearing effects (defer to LineEffect like single-player)
|
||||
if (game->hasCompletedLines() && lineEffect && !lineEffect->isActive()) {
|
||||
auto completedLines = game->getCompletedLines();
|
||||
lineEffect->startLineClear(completedLines, static_cast<int>(gridX), static_cast<int>(gridY), static_cast<int>(finalBlockSize));
|
||||
if (completedLines.size() == 4) {
|
||||
AppFireworks::spawn(gridX + GRID_W * 0.5f, gridY + GRID_H * 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
// Grid backdrop and border
|
||||
drawRectWithOffset(gridX - 3 - contentOffsetX, gridY - 3 - contentOffsetY, GRID_W + 6, GRID_H + 6, {100, 120, 200, 255});
|
||||
drawRectWithOffset(gridX - contentOffsetX, gridY - contentOffsetY, GRID_W, GRID_H, {20, 25, 35, 255});
|
||||
|
||||
// Divider line between halves (between columns 9 and 10)
|
||||
float dividerX = gridX + finalBlockSize * 10.0f;
|
||||
SDL_SetRenderDrawColor(renderer, 180, 210, 255, 235);
|
||||
SDL_FRect divider{ dividerX - 2.0f, gridY, 4.0f, GRID_H };
|
||||
SDL_RenderFillRect(renderer, ÷r);
|
||||
SDL_SetRenderDrawColor(renderer, 40, 80, 150, 140);
|
||||
SDL_FRect dividerGlow{ dividerX - 4.0f, gridY, 8.0f, GRID_H };
|
||||
SDL_RenderFillRect(renderer, ÷rGlow);
|
||||
|
||||
// Grid lines
|
||||
SDL_SetRenderDrawColor(renderer, 40, 45, 60, 255);
|
||||
for (int x = 1; x < CoopGame::COLS; ++x) {
|
||||
float lineX = gridX + x * finalBlockSize;
|
||||
SDL_RenderLine(renderer, lineX, gridY, lineX, gridY + GRID_H);
|
||||
}
|
||||
for (int y = 1; y < CoopGame::ROWS; ++y) {
|
||||
float lineY = gridY + y * finalBlockSize;
|
||||
SDL_RenderLine(renderer, gridX, lineY, gridX + GRID_W, lineY);
|
||||
}
|
||||
|
||||
// Half-row feedback: lightly tint rows where one side is filled, brighter where both are pending clear
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
const auto& rowStates = game->rowHalfStates();
|
||||
for (int y = 0; y < CoopGame::ROWS; ++y) {
|
||||
const auto& rs = rowStates[y];
|
||||
float rowY = gridY + y * finalBlockSize;
|
||||
|
||||
if (rs.leftFull && rs.rightFull) {
|
||||
SDL_SetRenderDrawColor(renderer, 140, 210, 255, 45);
|
||||
SDL_FRect fr{gridX, rowY, GRID_W, finalBlockSize};
|
||||
SDL_RenderFillRect(renderer, &fr);
|
||||
} else if (rs.leftFull ^ rs.rightFull) {
|
||||
SDL_SetRenderDrawColor(renderer, 90, 140, 220, 35);
|
||||
float w = GRID_W * 0.5f;
|
||||
float x = rs.leftFull ? gridX : gridX + w;
|
||||
SDL_FRect fr{x, rowY, w, finalBlockSize};
|
||||
SDL_RenderFillRect(renderer, &fr);
|
||||
}
|
||||
}
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
|
||||
|
||||
// Draw settled blocks
|
||||
const auto& board = game->boardRef();
|
||||
for (int y = 0; y < CoopGame::ROWS; ++y) {
|
||||
for (int x = 0; x < CoopGame::COLS; ++x) {
|
||||
const auto& cell = board[y * CoopGame::COLS + x];
|
||||
if (!cell.occupied || cell.value <= 0) continue;
|
||||
float px = gridX + x * finalBlockSize;
|
||||
float py = gridY + y * finalBlockSize;
|
||||
drawBlockTexturePublic(renderer, blocksTex, px, py, finalBlockSize, cell.value - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Active pieces (per-side smoothing)
|
||||
auto computeOffsets = [&](CoopGame::PlayerSide side, SmoothState& ss) {
|
||||
float offsetX = 0.0f;
|
||||
float offsetY = 0.0f;
|
||||
|
||||
if (smoothScrollEnabled) {
|
||||
const uint64_t seq = game->currentPieceSequence(side);
|
||||
const float targetX = static_cast<float>(game->current(side).x);
|
||||
if (!ss.initialized || ss.seq != seq) {
|
||||
ss.initialized = true;
|
||||
ss.seq = seq;
|
||||
ss.visualX = targetX;
|
||||
// Trigger a short spawn fade so the newly spawned piece visually
|
||||
// fades into the first visible row (like classic mode).
|
||||
SpawnFadeState &sf = (side == CoopGame::PlayerSide::Left) ? s_leftSpawnFade : s_rightSpawnFade;
|
||||
sf.active = true;
|
||||
sf.startTick = nowTicks;
|
||||
sf.durationMs = 200.0f;
|
||||
sf.seq = seq;
|
||||
sf.piece = game->current(side);
|
||||
sf.tileSize = finalBlockSize;
|
||||
// Target to first visible row (row 0)
|
||||
sf.targetX = gridX + static_cast<float>(sf.piece.x) * finalBlockSize;
|
||||
sf.targetY = gridY + 0.0f * finalBlockSize;
|
||||
} else {
|
||||
// Reuse exact horizontal smoothing from single-player
|
||||
constexpr float HORIZONTAL_SMOOTH_MS = 55.0f;
|
||||
const float lerpFactor = std::clamp(deltaMs / HORIZONTAL_SMOOTH_MS, 0.0f, 1.0f);
|
||||
ss.visualX = std::lerp(ss.visualX, targetX, lerpFactor);
|
||||
}
|
||||
offsetX = (ss.visualX - targetX) * finalBlockSize;
|
||||
|
||||
// Reuse exact single-player fall offset computation (per-side getters)
|
||||
double gravityMs = game->getGravityMs();
|
||||
if (gravityMs > 0.0) {
|
||||
double effectiveMs = game->isSoftDropping(side) ? std::max(5.0, gravityMs / 5.0) : gravityMs;
|
||||
double accumulator = std::clamp(game->getFallAccumulator(side), 0.0, effectiveMs);
|
||||
float progress = static_cast<float>(accumulator / effectiveMs);
|
||||
progress = std::clamp(progress, 0.0f, 1.0f);
|
||||
offsetY = progress * finalBlockSize;
|
||||
|
||||
// Clamp vertical offset to avoid overlapping settled blocks (same logic as single-player)
|
||||
const auto& boardRef = game->boardRef();
|
||||
const CoopGame::Piece& piece = game->current(side);
|
||||
float maxAllowed = finalBlockSize;
|
||||
for (int cy = 0; cy < 4; ++cy) {
|
||||
for (int cx = 0; cx < 4; ++cx) {
|
||||
if (!CoopGame::cellFilled(piece, cx, cy)) continue;
|
||||
int gx = piece.x + cx;
|
||||
int gy = piece.y + cy;
|
||||
if (gx < 0 || gx >= CoopGame::COLS) continue;
|
||||
int testY = gy + 1;
|
||||
int emptyRows = 0;
|
||||
if (testY < 0) {
|
||||
emptyRows -= testY;
|
||||
testY = 0;
|
||||
}
|
||||
while (testY >= 0 && testY < CoopGame::ROWS) {
|
||||
if (boardRef[testY * CoopGame::COLS + gx].occupied) break;
|
||||
++emptyRows;
|
||||
++testY;
|
||||
}
|
||||
float cellLimit = (emptyRows > 0) ? finalBlockSize : 0.0f;
|
||||
maxAllowed = std::min(maxAllowed, cellLimit);
|
||||
}
|
||||
}
|
||||
offsetY = std::min(offsetY, maxAllowed);
|
||||
}
|
||||
} else {
|
||||
ss.initialized = true;
|
||||
ss.seq = game->currentPieceSequence(side);
|
||||
ss.visualX = static_cast<float>(game->current(side).x);
|
||||
}
|
||||
|
||||
if (Settings::instance().isDebugEnabled()) {
|
||||
float dbg_targetX = static_cast<float>(game->current(side).x);
|
||||
double gMsDbg = game->getGravityMs();
|
||||
double accDbg = game->getFallAccumulator(side);
|
||||
int softDbg = game->isSoftDropping(side) ? 1 : 0;
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "COOP %s OFFSETS: seq=%llu visX=%.3f targX=%.3f offX=%.2f offY=%.2f gravMs=%.2f fallAcc=%.2f soft=%d",
|
||||
(side == CoopGame::PlayerSide::Left) ? "L" : "R",
|
||||
(unsigned long long)ss.seq,
|
||||
ss.visualX,
|
||||
dbg_targetX,
|
||||
offsetX,
|
||||
offsetY,
|
||||
gMsDbg,
|
||||
accDbg,
|
||||
softDbg
|
||||
);
|
||||
}
|
||||
return std::pair<float, float>{ offsetX, offsetY };
|
||||
};
|
||||
|
||||
// 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
|
||||
// real position is above the grid and will not be drawn by drawPiece.
|
||||
auto drawSpawnFadeIfActive = [&](SpawnFadeState &sf) {
|
||||
if (!sf.active) return;
|
||||
Uint32 now = SDL_GetTicks();
|
||||
float elapsed = static_cast<float>(now - sf.startTick);
|
||||
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));
|
||||
if (blocksTex) SDL_SetTextureAlphaMod(blocksTex, alpha);
|
||||
// Draw piece at target (first row)
|
||||
for (int cy = 0; cy < 4; ++cy) {
|
||||
for (int cx = 0; cx < 4; ++cx) {
|
||||
if (!CoopGame::cellFilled(sf.piece, cx, cy)) continue;
|
||||
float px = sf.targetX + static_cast<float>(cx) * sf.tileSize;
|
||||
float py = sf.targetY + static_cast<float>(cy) * sf.tileSize;
|
||||
drawBlockTexturePublic(renderer, blocksTex, px, py, sf.tileSize, sf.piece.type);
|
||||
}
|
||||
}
|
||||
if (blocksTex) SDL_SetTextureAlphaMod(blocksTex, 255);
|
||||
if (t >= 1.0f) sf.active = false;
|
||||
};
|
||||
|
||||
auto drawPiece = [&](const CoopGame::Piece& p, CoopGame::PlayerSide side, const std::pair<float,float>& offsets) {
|
||||
for (int cy = 0; cy < 4; ++cy) {
|
||||
for (int cx = 0; cx < 4; ++cx) {
|
||||
if (!CoopGame::cellFilled(p, cx, cy)) continue;
|
||||
int pxIdx = p.x + cx;
|
||||
int pyIdx = p.y + cy;
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto leftOffsets = computeOffsets(CoopGame::PlayerSide::Left, s_leftSmooth);
|
||||
const auto rightOffsets = computeOffsets(CoopGame::PlayerSide::Right, s_rightSmooth);
|
||||
// Draw transient spawn fades (if active) into the first visible row
|
||||
drawSpawnFadeIfActive(s_leftSpawnFade);
|
||||
drawSpawnFadeIfActive(s_rightSpawnFade);
|
||||
|
||||
// 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);
|
||||
}
|
||||
if (!(s_rightSpawnFade.active && s_rightSpawnFade.seq == game->currentPieceSequence(CoopGame::PlayerSide::Right))) {
|
||||
drawPiece(game->current(CoopGame::PlayerSide::Right), CoopGame::PlayerSide::Right, rightOffsets);
|
||||
}
|
||||
|
||||
// Next panels (two)
|
||||
const float nextPanelPad = 12.0f;
|
||||
const float nextPanelW = (GRID_W * 0.5f) - finalBlockSize * 1.5f;
|
||||
const float nextPanelH = NEXT_PANEL_HEIGHT - nextPanelPad * 2.0f;
|
||||
float nextLeftX = gridX + finalBlockSize;
|
||||
float nextRightX = gridX + GRID_W - finalBlockSize - nextPanelW;
|
||||
float nextY = contentStartY + contentOffsetY;
|
||||
|
||||
auto drawNextPanel = [&](float panelX, float panelY, const CoopGame::Piece& piece) {
|
||||
SDL_FRect panel{ panelX, panelY, nextPanelW, nextPanelH };
|
||||
if (nextPanelTex) {
|
||||
SDL_RenderTexture(renderer, nextPanelTex, nullptr, &panel);
|
||||
} else {
|
||||
drawRectWithOffset(panel.x - contentOffsetX, panel.y - contentOffsetY, panel.w, panel.h, SDL_Color{18,22,30,200});
|
||||
}
|
||||
// Center piece inside panel
|
||||
int minCx = 4, minCy = 4, maxCx = -1, maxCy = -1;
|
||||
for (int cy = 0; cy < 4; ++cy) {
|
||||
for (int cx = 0; cx < 4; ++cx) {
|
||||
if (!CoopGame::cellFilled(piece, cx, cy)) continue;
|
||||
minCx = std::min(minCx, cx);
|
||||
minCy = std::min(minCy, cy);
|
||||
maxCx = std::max(maxCx, cx);
|
||||
maxCy = std::max(maxCy, cy);
|
||||
}
|
||||
}
|
||||
if (maxCx >= minCx && maxCy >= minCy) {
|
||||
float tile = finalBlockSize * 0.8f;
|
||||
float pieceW = (maxCx - minCx + 1) * tile;
|
||||
float pieceH = (maxCy - minCy + 1) * tile;
|
||||
float startX = panel.x + (panel.w - pieceW) * 0.5f - minCx * tile;
|
||||
float startY = panel.y + (panel.h - pieceH) * 0.5f - minCy * tile;
|
||||
for (int cy = 0; cy < 4; ++cy) {
|
||||
for (int cx = 0; cx < 4; ++cx) {
|
||||
if (!CoopGame::cellFilled(piece, cx, cy)) continue;
|
||||
float px = startX + cx * tile;
|
||||
float py = startY + cy * tile;
|
||||
drawBlockTexturePublic(renderer, blocksTex, px, py, tile, piece.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
drawNextPanel(nextLeftX, nextY, game->next(CoopGame::PlayerSide::Left));
|
||||
drawNextPanel(nextRightX, nextY, game->next(CoopGame::PlayerSide::Right));
|
||||
|
||||
// Simple shared score text
|
||||
char buf[128];
|
||||
std::snprintf(buf, sizeof(buf), "SCORE %d LINES %d LEVEL %d", game->score(), game->lines(), game->level());
|
||||
pixelFont->draw(renderer, gridX + GRID_W * 0.5f - 140.0f, gridY + GRID_H + 24.0f, buf, 1.2f, SDL_Color{220, 230, 255, 255});
|
||||
}
|
||||
|
||||
void GameRenderer::renderExitPopup(
|
||||
SDL_Renderer* renderer,
|
||||
FontAtlas* pixelFont,
|
||||
|
||||
Reference in New Issue
Block a user