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)
|
// frame the whole window (right/bottom edges too)
|
||||||
XSetForeground(dpy, gc, C_DIM);
|
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
|
// food
|
||||||
if (foodx >= 0) {
|
if (foodx >= 0) {
|
||||||
@@ -363,6 +364,7 @@ int main() {
|
|||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(xfd, &fds);
|
FD_SET(xfd, &fds);
|
||||||
int r = select(xfd + 1, &fds, nullptr, nullptr, &tv);
|
int r = select(xfd + 1, &fds, nullptr, nullptr, &tv);
|
||||||
|
bool dirty = false;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
XEvent e;
|
XEvent e;
|
||||||
while (XPending(dpy)) {
|
while (XPending(dpy)) {
|
||||||
@@ -371,13 +373,15 @@ int main() {
|
|||||||
if (!running) break;
|
if (!running) break;
|
||||||
}
|
}
|
||||||
if (!running) break;
|
if (!running) break;
|
||||||
|
dirty = true;
|
||||||
}
|
}
|
||||||
if (running && now_ms() >= nextTick) {
|
if (running && now_ms() >= nextTick) {
|
||||||
if (state == PLAYING) step();
|
if (state == PLAYING) step();
|
||||||
if (state == PLAYING) nextTick = now_ms() + speed;
|
if (state == PLAYING) nextTick = now_ms() + speed;
|
||||||
else nextTick = now_ms() + 80; // keep loop responsive while paused/over
|
else nextTick = now_ms() + 80; // keep loop responsive while paused/over
|
||||||
render();
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
if (dirty) render();
|
||||||
}
|
}
|
||||||
|
|
||||||
XFreeGC(dpy, gc);
|
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) ----
|
// ---- tetromino definitions (state 0 within a 4x4 box) ----
|
||||||
// type 0..6 -> I,O,T,S,Z,J,L
|
// type 0..6 -> I,O,T,S,Z,J,L
|
||||||
static const int BASE[7][4][2] = {
|
// 7 tetrominoes, each with 4 explicit rotation states (classic SRS-style).
|
||||||
{{0,1},{1,1},{2,1},{3,1}}, // I
|
// type 0..6 -> I,O,T,S,Z,J,L. Coordinates are within a 4x4 box [x,y].
|
||||||
{{1,0},{2,0},{1,1},{2,1}}, // O
|
static const int SHAPES[7][4][4][2] = {
|
||||||
{{1,0},{0,1},{1,1},{2,1}}, // T
|
{ {{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},{0,1},{1,1}}, // S
|
{ {{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
|
||||||
{{0,0},{1,0},{1,1},{2,1}}, // Z
|
{ {{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
|
||||||
{{0,0},{0,1},{1,1},{2,1}}, // J
|
{ {{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
|
||||||
{{2,0},{0,1},{1,1},{2,1}}, // L
|
{ {{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 };
|
enum State { PLAYING, PAUSED, GAMEOVER };
|
||||||
@@ -112,16 +114,12 @@ static int speed = 1000; // ms per gravity step
|
|||||||
static unsigned long nextTick = 0;
|
static unsigned long nextTick = 0;
|
||||||
static bool running = true;
|
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]) {
|
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]) {
|
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() {
|
static void try_rotate() {
|
||||||
|
int nr = (curRot + 1) % 4;
|
||||||
int cells[4][2];
|
int cells[4][2];
|
||||||
piece_cells(curType, (curRot + 1) % 4, cells);
|
piece_cells(curType, nr, cells);
|
||||||
// simple wall kick: try shift left/right if blocked
|
// wall kicks: try in place, then small horizontal shifts
|
||||||
if (!collide(curX, curY, cells)) { curRot = (curRot + 1) % 4; return; }
|
int kicks[5] = {0, -1, 1, -2, 2};
|
||||||
if (!collide(curX - 1, curY, cells)) { curX--; curRot = (curRot + 1) % 4; return; }
|
for (int k = 0; k < 5; k++) {
|
||||||
if (!collide(curX + 1, curY, cells)) { curX++; curRot = (curRot + 1) % 4; return; }
|
if (!collide(curX + kicks[k], curY, cells)) {
|
||||||
if (!collide(curX - 2, curY, cells)) { curX -= 2; curRot = (curRot + 1) % 4; return; }
|
curX += kicks[k];
|
||||||
if (!collide(curX + 2, curY, cells)) { curX += 2; curRot = (curRot + 1) % 4; return; }
|
curRot = nr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset_game() {
|
static void reset_game() {
|
||||||
@@ -259,7 +261,8 @@ static void render() {
|
|||||||
|
|
||||||
// frame the whole window (right/bottom edges too)
|
// frame the whole window (right/bottom edges too)
|
||||||
XSetForeground(dpy, gc, C_DIM);
|
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
|
// settled blocks
|
||||||
for (int x = 0; x < PCOLS; x++)
|
for (int x = 0; x < PCOLS; x++)
|
||||||
@@ -445,6 +448,7 @@ int main() {
|
|||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(xfd, &fds);
|
FD_SET(xfd, &fds);
|
||||||
int r = select(xfd + 1, &fds, nullptr, nullptr, &tv);
|
int r = select(xfd + 1, &fds, nullptr, nullptr, &tv);
|
||||||
|
bool dirty = false;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
XEvent e;
|
XEvent e;
|
||||||
while (XPending(dpy)) {
|
while (XPending(dpy)) {
|
||||||
@@ -453,13 +457,15 @@ int main() {
|
|||||||
if (!running) break;
|
if (!running) break;
|
||||||
}
|
}
|
||||||
if (!running) break;
|
if (!running) break;
|
||||||
|
dirty = true;
|
||||||
}
|
}
|
||||||
if (running && now_ms() >= nextTick) {
|
if (running && now_ms() >= nextTick) {
|
||||||
if (state == PLAYING) step();
|
if (state == PLAYING) step();
|
||||||
if (state == PLAYING) nextTick = now_ms() + speed;
|
if (state == PLAYING) nextTick = now_ms() + speed;
|
||||||
else nextTick = now_ms() + 80;
|
else nextTick = now_ms() + 80;
|
||||||
render();
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
if (dirty) render();
|
||||||
}
|
}
|
||||||
|
|
||||||
XFreeGC(dpy, gc);
|
XFreeGC(dpy, gc);
|
||||||
|
|||||||
Reference in New Issue
Block a user