mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 15:17:48 +01:00
broken game over
This commit is contained in:
@@ -301,15 +301,14 @@ struct Font5x7 {
|
||||
case 'G':
|
||||
return 29;
|
||||
case 'P':
|
||||
return 30; // added
|
||||
return 30; // added earlier for PAUSED / GAME
|
||||
case 'D':
|
||||
return 31; // added
|
||||
return 31; // added earlier
|
||||
// space handled specially in drawText
|
||||
default:
|
||||
return 255;
|
||||
}
|
||||
}
|
||||
// columns × 7 rows (bit0 = top, bit6 = bottom)
|
||||
static const uint8_t data[32][5];
|
||||
};
|
||||
const uint8_t Font5x7::data[32][5] = {
|
||||
@@ -359,7 +358,7 @@ public:
|
||||
}
|
||||
|
||||
void render(const Board& b, int px, int py, int prot, int pidx, int score, int level, int lines, int nextIdx,
|
||||
bool ghost, bool paused) {
|
||||
bool ghost, bool paused, bool gameOver) {
|
||||
fb.clear(false); // white background
|
||||
drawBatteryOverlay();
|
||||
|
||||
@@ -413,7 +412,9 @@ public:
|
||||
if (cell_of(TET[nextIdx], 0, xx, yy))
|
||||
drawCellFullPreview(nx + xx * p, ny + yy * p, nextIdx + 1);
|
||||
|
||||
if (paused)
|
||||
if (gameOver)
|
||||
drawGameOverOverlay();
|
||||
else if (paused)
|
||||
drawPausedOverlay();
|
||||
DispTools::get().draw_to_display();
|
||||
}
|
||||
@@ -473,6 +474,32 @@ public:
|
||||
drawText(cx, cy, txt);
|
||||
}
|
||||
|
||||
void drawGameOverOverlay() {
|
||||
const char* l1 = "GAME"; // 4 letters
|
||||
const char* l2 = "OVER"; // 4 letters
|
||||
int w = 4 * 6 - 1; // width for 4 chars
|
||||
int h = 8; // font height
|
||||
int totalH = h * 2 + 2; // two lines + small gap
|
||||
int cx = (fb.width() - w) / 2;
|
||||
int cy = (fb.height() - totalH) / 2; // centered vertically
|
||||
int padX = 6, padY = 5;
|
||||
// Clear background region
|
||||
for (int y = -padY; y < totalH + padY; ++y)
|
||||
for (int x = -padX; x < w + padX; ++x)
|
||||
fb.drawPixel(cx + x, cy + y, false);
|
||||
// Border
|
||||
for (int x = -padX; x < w + padX; ++x) {
|
||||
fb.drawPixel(cx + x, cy - padY, true);
|
||||
fb.drawPixel(cx + x, cy + totalH + padY - 1, true);
|
||||
}
|
||||
for (int y = -padY; y < totalH + padY; ++y) {
|
||||
fb.drawPixel(cx - padX, cy + y, true);
|
||||
fb.drawPixel(cx + w + padX - 1, cy + y, true);
|
||||
}
|
||||
drawText(cx, cy, l1);
|
||||
drawText(cx, cy + h + 2, l2);
|
||||
}
|
||||
|
||||
private:
|
||||
IFramebuffer& fb;
|
||||
int ox = 0, oy = 0, bw = 0, bh = 0;
|
||||
@@ -777,15 +804,21 @@ public:
|
||||
void step() {
|
||||
const uint32_t now = clock.millis();
|
||||
overlayTick(now);
|
||||
InputState st = input.readState();
|
||||
if (!running) {
|
||||
// Allow restart on any button press (except no input)
|
||||
if (st.left || st.right || st.down || st.rotate || st.back) {
|
||||
restart();
|
||||
return; // restart sets dirty and running
|
||||
}
|
||||
if (dirty)
|
||||
paintHUD();
|
||||
return;
|
||||
}
|
||||
InputState st = input.readState();
|
||||
// Pause toggle
|
||||
if (st.back && !backPrev) {
|
||||
paused = !paused;
|
||||
PowerHelper::get().set_slow(paused); // engage/disengage slow mode
|
||||
PowerHelper::get().set_slow(paused);
|
||||
dirty = true;
|
||||
}
|
||||
backPrev = st.back;
|
||||
@@ -794,18 +827,15 @@ public:
|
||||
paintHUD();
|
||||
return;
|
||||
}
|
||||
|
||||
// Rotation
|
||||
if (st.rotate && !rotPrev) {
|
||||
if (tryRotate(+1))
|
||||
dirty = true;
|
||||
}
|
||||
rotPrev = st.rotate;
|
||||
|
||||
// Horizontal movement + autorepeat
|
||||
// Horizontal
|
||||
handleHorizontal(st, now);
|
||||
|
||||
// Gravity / soft drop
|
||||
// Gravity
|
||||
const int g = st.down ? cfg::DropFastMs : score.dropMs;
|
||||
if (elapsed_ms(lastFall, now) >= (uint32_t) g) {
|
||||
lastFall = now;
|
||||
@@ -816,11 +846,9 @@ public:
|
||||
} else if (elapsed_ms(touchTime, now) >= (uint32_t) cfg::LockDelayMs) {
|
||||
lockAndAdvance();
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
touchingGround = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
paintHUD();
|
||||
}
|
||||
@@ -831,12 +859,11 @@ public:
|
||||
if (paused || !running) {
|
||||
uint32_t untilOverlay =
|
||||
(lastOverlayUpd + overlayIntervalMs > now) ? (lastOverlayUpd + overlayIntervalMs - now) : 0;
|
||||
uint32_t cap = paused ? 2000u : 500u; // allow longer idle while paused
|
||||
uint32_t cap = (paused || !running) ? 2000u : 500u;
|
||||
if (untilOverlay > cap)
|
||||
untilOverlay = cap;
|
||||
return untilOverlay;
|
||||
}
|
||||
|
||||
// Estimate next gravity event
|
||||
uint32_t g = score.dropMs;
|
||||
uint32_t sinceFall = elapsed_ms(lastFall, now);
|
||||
@@ -846,20 +873,14 @@ public:
|
||||
uint32_t untilLock = (sinceTouch >= (uint32_t) cfg::LockDelayMs) ? 0 : (cfg::LockDelayMs - sinceTouch);
|
||||
untilDrop = std::min(untilDrop, untilLock);
|
||||
}
|
||||
|
||||
// Overlay may force earlier wake
|
||||
uint32_t untilOverlay =
|
||||
(lastOverlayUpd + overlayIntervalMs > now) ? (lastOverlayUpd + overlayIntervalMs - now) : 0;
|
||||
uint32_t sleep = std::min(untilDrop, untilOverlay);
|
||||
|
||||
// Provide a max sleep to keep input latency reasonable
|
||||
if (sleep > maxIdleSleepMs)
|
||||
sleep = maxIdleSleepMs;
|
||||
return sleep;
|
||||
}
|
||||
|
||||
bool isPaused() const { return paused; }
|
||||
|
||||
private:
|
||||
// Power-aware overlay throttling
|
||||
static constexpr uint32_t overlayIntervalMs = 500; // base interval (paused uses multiplier)
|
||||
@@ -882,17 +903,43 @@ private:
|
||||
uint32_t lastOverlayUpd = 0;
|
||||
|
||||
void overlayTick(uint32_t now) {
|
||||
uint32_t interval = paused ? overlayIntervalMs * 8 : overlayIntervalMs; // 4s when paused
|
||||
uint32_t interval = (paused || !running) ? overlayIntervalMs * 8 : overlayIntervalMs; // 4s paused or game over
|
||||
if (elapsed_ms(lastOverlayUpd, now) >= interval) {
|
||||
lastOverlayUpd = now;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void paintHUD() {
|
||||
renderer.render(board, px, py, rot, current, score.score, score.level, score.lines, nextPiece, true, paused);
|
||||
renderer.render(board, px, py, rot, current, score.score, score.level, score.lines, nextPiece, true, paused,
|
||||
!running);
|
||||
dirty = false;
|
||||
}
|
||||
void restart() {
|
||||
board.clear();
|
||||
score = ScoreState{};
|
||||
bag = Bag{}; // new sequence
|
||||
nextPiece = bag.next();
|
||||
running = true;
|
||||
paused = false;
|
||||
touchingGround = false;
|
||||
rotPrev = lHeld = rHeld = backPrev = false;
|
||||
PowerHelper::get().set_slow(false);
|
||||
spawn();
|
||||
dirty = true;
|
||||
}
|
||||
void spawn() {
|
||||
current = nextPiece;
|
||||
nextPiece = bag.next();
|
||||
px = 3;
|
||||
py = -2;
|
||||
rot = 0; // spawn above board
|
||||
touchingGround = false;
|
||||
dirty = true;
|
||||
// Game over if immediate collision at spawn position OR unable to move one row down
|
||||
if (board.collides(px, py, rot, current) || board.collides(px, py + 1, rot, current)) {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool tryMoveInternal(int dx, int dy) {
|
||||
if (!board.collides(px + dx, py + dy, rot, current)) {
|
||||
@@ -903,12 +950,10 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool tryMove(int dx, int dy) { return tryMoveInternal(dx, dy); }
|
||||
|
||||
bool tryRotate(int d) {
|
||||
int nr = (rot + d + 4) % 4;
|
||||
static const int kicks[][2] = {{0, 0}, {-1, 0}, {1, 0}, {-2, 0}, {2, 0}, {0, -1}};
|
||||
for (auto& k: kicks)
|
||||
for (auto& k: kicks) {
|
||||
if (!board.collides(px + k[0], py + k[1], nr, current)) {
|
||||
px += k[0];
|
||||
py += k[1];
|
||||
@@ -916,6 +961,7 @@ private:
|
||||
dirty = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void handleHorizontal(const InputState& st, uint32_t now) {
|
||||
@@ -939,7 +985,6 @@ private:
|
||||
}
|
||||
} else
|
||||
lHeld = false;
|
||||
|
||||
if (st.right) {
|
||||
if (!rHeld) {
|
||||
rHeld = true;
|
||||
@@ -958,8 +1003,21 @@ private:
|
||||
rHeld = false;
|
||||
}
|
||||
void lockAndAdvance() {
|
||||
// Check if any part of current piece is above visible area before locking (for classic top-out)
|
||||
bool above = false;
|
||||
for (int yy = 0; yy < 4 && !above; ++yy)
|
||||
for (int xx = 0; xx < 4 && !above; ++xx)
|
||||
if (cell_of(TET[current], rot, xx, yy)) {
|
||||
int gy = py + yy;
|
||||
if (gy < 0)
|
||||
above = true;
|
||||
}
|
||||
board.lock(px, py, rot, current);
|
||||
dirty = true;
|
||||
if (above) { // immediate game over (do not spawn new piece)
|
||||
running = false;
|
||||
return;
|
||||
}
|
||||
int c = board.clearLines();
|
||||
if (c) {
|
||||
static const int pts[5] = {0, 100, 300, 500, 800};
|
||||
@@ -973,16 +1031,6 @@ private:
|
||||
}
|
||||
spawn();
|
||||
}
|
||||
void spawn() {
|
||||
current = nextPiece;
|
||||
nextPiece = bag.next();
|
||||
px = 3;
|
||||
py = -2;
|
||||
rot = 0;
|
||||
dirty = true;
|
||||
if (board.collides(px, py, rot, current))
|
||||
running = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user