broken game over

This commit is contained in:
2025-10-06 09:47:50 +02:00
parent 8b8d9d3a55
commit e389a776be

View File

@@ -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;
}
};
// ─────────────────────────────────────────────────────────────────────────────