fix(games): correct Tetris rotation, fix input/render lag, visible window frame
- Tetris: replace pivot-based rotation with explicit 4-state SHAPES table (classic SRS-style) so rotation is correct and the O piece no longer drifts on rotate; keep simple wall-kick offsets. - Both: render immediately after processing input (not only on gravity steps) so rotation/movement is no longer laggy. - Both: draw the window frame inset by 2-3px (double line) so the right and bottom edges are actually visible instead of clipped at the edge.
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6
-2
@@ -219,7 +219,8 @@ static void render() {
|
||||
|
||||
// frame the whole window (right/bottom edges too)
|
||||
XSetForeground(dpy, gc, C_DIM);
|
||||
XDrawRectangle(dpy, back, gc, 0, 0, winW - 1, winH - 1);
|
||||
XDrawRectangle(dpy, back, gc, 2, 2, winW - 4, winH - 4);
|
||||
XDrawRectangle(dpy, back, gc, 3, 3, winW - 6, winH - 6);
|
||||
|
||||
// food
|
||||
if (foodx >= 0) {
|
||||
@@ -363,6 +364,7 @@ int main() {
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(xfd, &fds);
|
||||
int r = select(xfd + 1, &fds, nullptr, nullptr, &tv);
|
||||
bool dirty = false;
|
||||
if (r > 0) {
|
||||
XEvent e;
|
||||
while (XPending(dpy)) {
|
||||
@@ -371,13 +373,15 @@ int main() {
|
||||
if (!running) break;
|
||||
}
|
||||
if (!running) break;
|
||||
dirty = true;
|
||||
}
|
||||
if (running && now_ms() >= nextTick) {
|
||||
if (state == PLAYING) step();
|
||||
if (state == PLAYING) nextTick = now_ms() + speed;
|
||||
else nextTick = now_ms() + 80; // keep loop responsive while paused/over
|
||||
render();
|
||||
dirty = true;
|
||||
}
|
||||
if (dirty) render();
|
||||
}
|
||||
|
||||
XFreeGC(dpy, gc);
|
||||
|
||||
+32
-26
@@ -92,14 +92,16 @@ static unsigned long C_BG, C_FG, C_HEAD, C_FOOD, C_DIM;
|
||||
|
||||
// ---- tetromino definitions (state 0 within a 4x4 box) ----
|
||||
// type 0..6 -> I,O,T,S,Z,J,L
|
||||
static const int BASE[7][4][2] = {
|
||||
{{0,1},{1,1},{2,1},{3,1}}, // I
|
||||
{{1,0},{2,0},{1,1},{2,1}}, // O
|
||||
{{1,0},{0,1},{1,1},{2,1}}, // T
|
||||
{{1,0},{2,0},{0,1},{1,1}}, // S
|
||||
{{0,0},{1,0},{1,1},{2,1}}, // Z
|
||||
{{0,0},{0,1},{1,1},{2,1}}, // J
|
||||
{{2,0},{0,1},{1,1},{2,1}}, // L
|
||||
// 7 tetrominoes, each with 4 explicit rotation states (classic SRS-style).
|
||||
// type 0..6 -> I,O,T,S,Z,J,L. Coordinates are within a 4x4 box [x,y].
|
||||
static const int SHAPES[7][4][4][2] = {
|
||||
{ {{0,1},{1,1},{2,1},{3,1}}, {{2,0},{2,1},{2,2},{2,3}}, {{0,2},{1,2},{2,2},{3,2}}, {{1,0},{1,1},{1,2},{1,3}} }, // I
|
||||
{ {{1,0},{2,0},{1,1},{2,1}}, {{1,0},{2,0},{1,1},{2,1}}, {{1,0},{2,0},{1,1},{2,1}}, {{1,0},{2,0},{1,1},{2,1}} }, // O
|
||||
{ {{1,0},{0,1},{1,1},{2,1}}, {{1,0},{1,1},{2,1},{1,2}}, {{0,1},{1,1},{2,1},{1,2}}, {{1,0},{0,1},{1,1},{1,2}} }, // T
|
||||
{ {{1,0},{2,0},{0,1},{1,1}}, {{1,0},{1,1},{2,1},{2,2}}, {{1,1},{2,1},{0,2},{1,2}}, {{0,0},{0,1},{1,1},{1,2}} }, // S
|
||||
{ {{0,0},{1,0},{1,1},{2,1}}, {{2,0},{1,1},{2,1},{1,2}}, {{0,1},{1,1},{1,2},{2,2}}, {{1,0},{0,1},{1,1},{0,2}} }, // Z
|
||||
{ {{0,0},{0,1},{1,1},{2,1}}, {{1,0},{2,0},{1,1},{1,2}}, {{0,1},{1,1},{2,1},{2,2}}, {{1,0},{1,1},{0,2},{1,2}} }, // J
|
||||
{ {{2,0},{0,1},{1,1},{2,1}}, {{1,0},{1,1},{1,2},{2,2}}, {{0,1},{1,1},{2,1},{0,2}}, {{0,0},{1,0},{1,1},{1,2}} }, // L
|
||||
};
|
||||
|
||||
enum State { PLAYING, PAUSED, GAMEOVER };
|
||||
@@ -112,16 +114,12 @@ static int speed = 1000; // ms per gravity step
|
||||
static unsigned long nextTick = 0;
|
||||
static bool running = true;
|
||||
|
||||
static void rotate_cells(const int src[4][2], int dst[4][2], int times) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int x = src[i][0], y = src[i][1];
|
||||
for (int t = 0; t < times; t++) { int nx = 3 - y; int ny = x; x = nx; y = ny; }
|
||||
dst[i][0] = x; dst[i][1] = y;
|
||||
}
|
||||
}
|
||||
|
||||
static void piece_cells(int type, int rot, int out[4][2]) {
|
||||
rotate_cells(BASE[type], out, ((rot % 4) + 4) % 4);
|
||||
int r = ((rot % 4) + 4) % 4;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
out[i][0] = SHAPES[type][r][i][0];
|
||||
out[i][1] = SHAPES[type][r][i][1];
|
||||
}
|
||||
}
|
||||
|
||||
static bool collide(int px, int py, int cells[4][2]) {
|
||||
@@ -214,14 +212,18 @@ static void try_move(int dx) {
|
||||
}
|
||||
|
||||
static void try_rotate() {
|
||||
int nr = (curRot + 1) % 4;
|
||||
int cells[4][2];
|
||||
piece_cells(curType, (curRot + 1) % 4, cells);
|
||||
// simple wall kick: try shift left/right if blocked
|
||||
if (!collide(curX, curY, cells)) { curRot = (curRot + 1) % 4; return; }
|
||||
if (!collide(curX - 1, curY, cells)) { curX--; curRot = (curRot + 1) % 4; return; }
|
||||
if (!collide(curX + 1, curY, cells)) { curX++; curRot = (curRot + 1) % 4; return; }
|
||||
if (!collide(curX - 2, curY, cells)) { curX -= 2; curRot = (curRot + 1) % 4; return; }
|
||||
if (!collide(curX + 2, curY, cells)) { curX += 2; curRot = (curRot + 1) % 4; return; }
|
||||
piece_cells(curType, nr, cells);
|
||||
// wall kicks: try in place, then small horizontal shifts
|
||||
int kicks[5] = {0, -1, 1, -2, 2};
|
||||
for (int k = 0; k < 5; k++) {
|
||||
if (!collide(curX + kicks[k], curY, cells)) {
|
||||
curX += kicks[k];
|
||||
curRot = nr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_game() {
|
||||
@@ -259,7 +261,8 @@ static void render() {
|
||||
|
||||
// frame the whole window (right/bottom edges too)
|
||||
XSetForeground(dpy, gc, C_DIM);
|
||||
XDrawRectangle(dpy, back, gc, 0, 0, winW - 1, winH - 1);
|
||||
XDrawRectangle(dpy, back, gc, 2, 2, winW - 4, winH - 4);
|
||||
XDrawRectangle(dpy, back, gc, 3, 3, winW - 6, winH - 6);
|
||||
|
||||
// settled blocks
|
||||
for (int x = 0; x < PCOLS; x++)
|
||||
@@ -445,6 +448,7 @@ int main() {
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(xfd, &fds);
|
||||
int r = select(xfd + 1, &fds, nullptr, nullptr, &tv);
|
||||
bool dirty = false;
|
||||
if (r > 0) {
|
||||
XEvent e;
|
||||
while (XPending(dpy)) {
|
||||
@@ -453,13 +457,15 @@ int main() {
|
||||
if (!running) break;
|
||||
}
|
||||
if (!running) break;
|
||||
dirty = true;
|
||||
}
|
||||
if (running && now_ms() >= nextTick) {
|
||||
if (state == PLAYING) step();
|
||||
if (state == PLAYING) nextTick = now_ms() + speed;
|
||||
else nextTick = now_ms() + 80;
|
||||
render();
|
||||
dirty = true;
|
||||
}
|
||||
if (dirty) render();
|
||||
}
|
||||
|
||||
XFreeGC(dpy, gc);
|
||||
|
||||
Reference in New Issue
Block a user