summary refs log tree commit diff
diff options
context:
space:
mode:
authorturnipgod2025-03-04 14:34:34 -0500
committerturnipgod2025-03-04 14:34:34 -0500
commitfd7f9092da2cb1bc85161263b985c3f0f45e7302 (patch)
treeb2b0649597c645cfdc8b8cb442d25f495e235bf6
parentdf24be9450f1221eea839b3c64982a0624627970 (diff)
major refactor using a state machine to prepare for animations later
-rw-r--r--build.ninja1
-rw-r--r--src/main.c423
2 files changed, 226 insertions, 198 deletions
diff --git a/build.ninja b/build.ninja
index a173608..7166436 100644
--- a/build.ninja
+++ b/build.ninja
@@ -14,4 +14,3 @@ build out/tg_tetris: ld obj/main.o
         ldflags = -lraylib -lm
 
 build obj/main.o: cc src/main.c
-        
diff --git a/src/main.c b/src/main.c
index 9ef03f0..fc42c4f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,8 +11,8 @@
 
 #define BLOCK_SIZE 25
 
-#define FALL_TICK 0.2
-#define SOFT_DROP_MULT 4
+#define FALL_TICK 0.3
+#define SOFT_DROP_MULT 4.0
 #define LOCK_TICK 0.5
 
 enum Phases {
@@ -20,13 +20,19 @@ enum Phases {
     FALL,
     LOCK,
     PATTERN,
-    MARK,
-    ITERATE,
-    ANIMATE,
-    ELIMINATE,
-    COMPLETE
+    //MARK,
+    //ITERATE,
+    //ANIMATE,
+    //ELIMINATE,
+    //COMPLETE
 } game_phase;
 
+enum InputResult {
+    NONE = 0,
+    MOVED,
+    HARD_DROPPED
+};
+
 #include "tetromino.h"
 
 struct {
@@ -42,8 +48,8 @@ int current_bag = 0;
 int current_piece_index = 0;
 int hold = 7;
 int held = 0;
-int hard_drop = 0;
-double phase_time = 0.0;
+double phase_time;
+double fall_tick;
 
 void draw_block(int x, int y, int width, int height, struct Color color)
 {
@@ -95,48 +101,50 @@ void render_matrix(void)
         }
     }
 
-    /* draw ghost piece */
     int ghost_y = 0;
-    while(!is_overlap(0, ghost_y)) {
-        ghost_y--;
-    }
+    if (game_phase == FALL || game_phase == LOCK) {
+        /* draw ghost piece */
+        while(!is_overlap(0, ghost_y)) {
+            ghost_y--;
+        }
 
-    for (int i = 0; i < tetrominos[current_piece.type].size; i++) {
-        for (int j = 0; j < tetrominos[current_piece.type].size; j++) {
-            if (tetrominos[current_piece.type].directions[current_piece.rotation][j][i] == 1) {
-                draw_block(
-                    (matrix_origin_x + ((current_piece.pos_x + i - 1) * BLOCK_SIZE)) + 3
-                    ,(matrix_origin_y + ((current_piece.pos_y + j + ghost_y) * BLOCK_SIZE)) + 3
-                    ,BLOCK_SIZE - 6
-                    ,BLOCK_SIZE - 6
-                    ,tetrominos[current_piece.type].color
-                );
-                draw_block(
-                    (matrix_origin_x + ((current_piece.pos_x + i - 1) * BLOCK_SIZE)) + 4
-                    ,(matrix_origin_y + ((current_piece.pos_y + j + ghost_y) * BLOCK_SIZE)) + 4
-                    ,BLOCK_SIZE - 8
-                    ,BLOCK_SIZE - 8
-                    ,BLACK
-                );
+        for (int i = 0; i < tetrominos[current_piece.type].size; i++) {
+            for (int j = 0; j < tetrominos[current_piece.type].size; j++) {
+                if (tetrominos[current_piece.type].directions[current_piece.rotation][j][i] == 1) {
+                    draw_block(
+                        (matrix_origin_x + ((current_piece.pos_x + i - 1) * BLOCK_SIZE)) + 3
+                        ,(matrix_origin_y + ((current_piece.pos_y + j + ghost_y) * BLOCK_SIZE)) + 3
+                        ,BLOCK_SIZE - 6
+                        ,BLOCK_SIZE - 6
+                        ,tetrominos[current_piece.type].color
+                    );
+                    draw_block(
+                        (matrix_origin_x + ((current_piece.pos_x + i - 1) * BLOCK_SIZE)) + 4
+                        ,(matrix_origin_y + ((current_piece.pos_y + j + ghost_y) * BLOCK_SIZE)) + 4
+                        ,BLOCK_SIZE - 8
+                        ,BLOCK_SIZE - 8
+                        ,BLACK
+                    );
+                }
             }
         }
-    }
-
-    /* draw current piece */
-    for (int i = 0; i < tetrominos[current_piece.type].size; i++) {
-        for (int j = 0; j < tetrominos[current_piece.type].size; j++) {
-            if (tetrominos[current_piece.type].directions[current_piece.rotation][j][i] == 1) {
-                draw_block(
-                    (matrix_origin_x + ((current_piece.pos_x + i - 1) * BLOCK_SIZE)) + 1
-                    ,(matrix_origin_y + ((current_piece.pos_y + j - 1) * BLOCK_SIZE)) + 1
-                    ,BLOCK_SIZE - 2
-                    ,BLOCK_SIZE - 2
-                    ,tetrominos[current_piece.type].color
-                );
+        /* draw current piece */
+        for (int i = 0; i < tetrominos[current_piece.type].size; i++) {
+            for (int j = 0; j < tetrominos[current_piece.type].size; j++) {
+                if (tetrominos[current_piece.type].directions[current_piece.rotation][j][i] == 1) {
+                    draw_block(
+                        (matrix_origin_x + ((current_piece.pos_x + i - 1) * BLOCK_SIZE)) + 1
+                        ,(matrix_origin_y + ((current_piece.pos_y + j - 1) * BLOCK_SIZE)) + 1
+                        ,BLOCK_SIZE - 2
+                        ,BLOCK_SIZE - 2
+                        ,tetrominos[current_piece.type].color
+                    );
+                }
             }
         }
     }
 
+
     /* draw piece hold */
     draw_block(
         SCREEN_WIDTH/2 + 1 - (BLOCK_SIZE),
@@ -203,6 +211,9 @@ void shuffle_bag(int *bag)
 
 void generation_phase(void)
 {
+    if (phase_time < 0.2) {
+        return;
+    }
     // bag generation
     if (current_piece_index == 14) {
         shuffle_bag(&(piece_queue[7]));
@@ -217,14 +228,73 @@ void generation_phase(void)
     current_piece.rotation = NORTH;
     current_piece.pos_x = 4;
     current_piece.pos_y = 20;
-    if (is_overlap(0,0)) {
-        CloseWindow();
+    held = 0;
+    phase_time = 0.0;
+    game_phase = FALL;
+}
+
+int rotate_piece(int dir)
+{
+    int *kick_table;
+    int i;
+    int can_rotate = 0;
+
+    if (dir == 1) {
+        kick_table = tetrominos[current_piece.type].cw_kick_table[current_piece.rotation];
+    } else if (dir == -1) {
+        kick_table = tetrominos[current_piece.type].ccw_kick_table[current_piece.rotation];
+    }
+
+    current_piece.rotation += dir;
+    if (current_piece.rotation < 0) {
+        current_piece.rotation = 3;
+    } else {
+        current_piece.rotation = current_piece.rotation % 4;
+    }
+
+    for (i = 0; i < 10 && !can_rotate; i += 2) {
+        if (!is_overlap(kick_table[i], kick_table[i+1])) {
+            current_piece.pos_x += kick_table[i];
+            current_piece.pos_y += kick_table[i+1];
+            can_rotate = 1;
+        }
     }
+
+    if (!can_rotate) {
+        current_piece.rotation -= dir;
+        if (current_piece.rotation < 0) {
+            current_piece.rotation = 3;
+        } else {
+            current_piece.rotation = current_piece.rotation % 4;
+        }
+    }
+
+    return can_rotate;
+}
+
+int move_piece(int dir)
+{
+    if (is_overlap(dir, 0)) {
+        return 0;
+    }
+    current_piece.pos_x += dir;
+    return 1;
 }
 
-void handle_hold(void)
+void move_piece_to_matrix(void)
 {
-    if (IsKeyDown(KEY_SPACE) && !held) {
+    for (int i = 0; i < tetrominos[current_piece.type].size; i++) {
+        for (int j = 0; j < tetrominos[current_piece.type].size; j++) {
+            if (tetrominos[current_piece.type].directions[current_piece.rotation][j][i] == 1) {
+                matrix[current_piece.pos_x + i - 1][current_piece.pos_y + j - 1] = tetrominos[current_piece.type].color;
+            }
+        }
+    }
+}
+
+void hold_piece(void)
+{
+    if (!held) {
         if (hold == 7) {
             hold = current_piece.type;
             current_piece.type = piece_queue[current_piece_index];
@@ -235,6 +305,7 @@ void handle_hold(void)
             current_piece.type = hold;
             hold = temp;
         }
+        phase_time = 0.0;
         game_phase = FALL;
         current_piece.rotation = NORTH;
         current_piece.pos_x = 4;
@@ -246,165 +317,108 @@ void handle_hold(void)
     }
 }
 
-void handle_rotate(void)
-{
-    static int rotated_cw = 0;
-    static int rotated_ccw = 0;
-    if (game_phase == FALL || game_phase == LOCK) {
-        int *kick_table;
-        int can_rotate = 0;
-        int i;
-        if (IsKeyDown(KEY_L) && !rotated_cw) {
-            kick_table = tetrominos[current_piece.type].cw_kick_table[current_piece.rotation];
-            current_piece.rotation++;
-            current_piece.rotation = current_piece.rotation % 4;
-            for (i = 0; i < 10 && !can_rotate; i += 2) {
-                if (!is_overlap(kick_table[i], kick_table[i+1])) {
-                    current_piece.pos_x += kick_table[i];
-                    current_piece.pos_y += kick_table[i+1];
-                    can_rotate = 1;
-                    if (game_phase == LOCK) {
-                        phase_time = 0.0;
-                    }
-                }
-            }
-            if (!can_rotate) {
-                current_piece.rotation--;
-                if (current_piece.rotation < 0) current_piece.rotation = 3;
-            }
-            rotated_cw = 1;
-        }
-        if (IsKeyUp(KEY_L)) {
-            rotated_cw = 0;
-        }
-        if (IsKeyDown(KEY_J) && !rotated_ccw) {
-            kick_table = tetrominos[current_piece.type].ccw_kick_table[current_piece.rotation];
-            current_piece.rotation--;
-            if (current_piece.rotation < 0) current_piece.rotation = 3;
-            for (i = 0; i < 10 && !can_rotate; i += 2) {
-                if (!is_overlap(kick_table[i], kick_table[i+1])) {
-                    current_piece.pos_x += kick_table[i];
-                    current_piece.pos_y += kick_table[i+1];
-                    can_rotate = 1;
-                    if (game_phase == LOCK) {
-                        phase_time = 0.0;
-                    }
-                }
-            }
-            if (!can_rotate) {
-                current_piece.rotation++;
-                current_piece.rotation = current_piece.rotation % 4;
-            }
-            rotated_ccw = 1;
-        }
-        if (IsKeyUp(KEY_J)) {
-            rotated_ccw = 0;
+int handle_input(void) {
+    static int moved_l = 0;
+    static int moved_r = 0;
+    static int moved_cw = 0;
+    static int moved_ccw = 0;
+    static int hard_dropped = 0;
+
+    if (IsKeyDown(KEY_A)) {
+        if (!moved_l) {
+            moved_l = move_piece(-1);
+            return MOVED;
         }
+    } else {
+        moved_l = 0;
     }
-}
 
-void move_right(void)
-{
-    if (is_overlap(1, 0)) {
-        return;
-    }
-    if (game_phase == LOCK) {
-        phase_time = 0.0;
+    if (IsKeyDown(KEY_D)) {
+        if (!moved_r) {
+            moved_r = move_piece(+1);
+            return MOVED;
+        }
+    } else {
+        moved_r = 0;
     }
-    current_piece.pos_x++;
-}
 
-void move_left(void)
-{
-    if (is_overlap(-1, 0)) {
-        return;
-    }
-    if (game_phase == LOCK) {
-        phase_time = 0.0;
+    if (IsKeyDown(KEY_L)) {
+        if (!moved_cw) {
+            moved_cw = rotate_piece(+1);
+            return MOVED;
+        }
+    } else {
+        moved_cw = 0;
     }
-    current_piece.pos_x--;
-}
 
-void handle_lr(void)
-{
-    static int moved_right = 0;
-    static int moved_left = 0;
-    if (game_phase == FALL || game_phase == LOCK) {
-        if (IsKeyDown(KEY_D) && !moved_right) {
-            move_right();
-            moved_right = 1;
-        }
-        if (IsKeyUp(KEY_D)) {
-            moved_right = 0;
-        }
-        if (IsKeyDown(KEY_A) && !moved_left) {
-            move_left();
-            moved_left = 1;
-        }
-        if (IsKeyUp(KEY_A)) {
-            moved_left = 0;
+    if (IsKeyDown(KEY_J)) {
+        if (!moved_ccw) {
+            moved_ccw = rotate_piece(-1);
+            return MOVED;
         }
+    } else {
+        moved_ccw = 0;
     }
-}
 
-void move_piece_to_matrix(void)
-{
-    for (int i = 0; i < tetrominos[current_piece.type].size; i++) {
-        for (int j = 0; j < tetrominos[current_piece.type].size; j++) {
-            if (tetrominos[current_piece.type].directions[current_piece.rotation][j][i] == 1) {
-                matrix[current_piece.pos_x + i - 1][current_piece.pos_y + j - 1] = tetrominos[current_piece.type].color;
+    if (IsKeyDown(KEY_W)) {
+        if (!hard_dropped) {
+            hard_dropped = 1;
+            while (!is_overlap(0, -1)) {
+                current_piece.pos_y--;
             }
+            return HARD_DROPPED;
         }
+    } else {
+        hard_dropped = 0;
     }
-}
 
-void handle_fall(void)
-{
-    phase_time += GetFrameTime();
-    if (game_phase == FALL) {
-        if (!hard_drop && ((!IsKeyDown(KEY_S) && phase_time < FALL_TICK) || phase_time < FALL_TICK / SOFT_DROP_MULT)) {
-            return;
-        } else {
-            phase_time = 0.0;
-        }
+    if (IsKeyDown(KEY_S)) {
+        fall_tick = FALL_TICK / SOFT_DROP_MULT;
+    } else {
+        fall_tick = FALL_TICK;
     }
 
-    if (is_overlap(0, -1)) {
-        game_phase = LOCK;
-    } else {
-        game_phase = FALL;
-        current_piece.pos_y--;
+    if (IsKeyDown(KEY_SPACE)) {
+        hold_piece();
     }
 
-    if (is_overlap(0, -1) && (phase_time >= LOCK_TICK || hard_drop)) {
-        move_piece_to_matrix();
-        generation_phase();
-        held = 0;
+    return NONE;
+}
+
+void lock_phase(void) {
+    enum InputResult action = handle_input();
+    if (action == MOVED) {
+        phase_time = 0.0;
+    }
+    if (!is_overlap(0, -1)) {
+        phase_time = 0.0;
         game_phase = FALL;
+    } else if (phase_time >= LOCK_TICK || action == HARD_DROPPED) {
+        move_piece_to_matrix();
         phase_time = 0.0;
-        hard_drop = 0;
-        return;
-    } 
+        game_phase = PATTERN;
+    }
 }
 
-void handle_hard_drop(void)
+void fall_phase(void)
 {
-    static int dropped = 0;
-    if (game_phase == FALL || game_phase == LOCK) {
-        if (IsKeyDown(KEY_W) && !dropped) {
-            dropped = 1;
-            hard_drop = 1;
-            while(!is_overlap(0, -1)) {
-                current_piece.pos_y--;
-            }
-        }
-        if (IsKeyUp(KEY_W)) {
-            dropped = 0;
+    enum InputResult action = handle_input();
+    if (action == HARD_DROPPED) {
+        phase_time = 0.0;
+        game_phase = PATTERN;
+    } else {
+        if (is_overlap(0, -1)) {
+            phase_time = 0.0;
+            game_phase = LOCK;
+        } else if (phase_time >= fall_tick || action == HARD_DROPPED) {
+            phase_time = 0.0;
+            current_piece.pos_y--;
         }
     }
 }
 
-void clear_line(int line) {
+void clear_line(int line)
+{
     for (int y = line; y < MATRIX_HEIGHT - 1; y++) {
         for (int x = 0; x < MATRIX_WIDTH; x++) {
             matrix[x][y] = matrix[x][y+1];
@@ -415,8 +429,9 @@ void clear_line(int line) {
     }
 }
 
-void handle_line_clears(void)
+void pattern_phase(void)
 {
+    move_piece_to_matrix();
     for (int y = 0; y < MATRIX_HEIGHT; y++) {
         int line_full = 1;
         for (int x = 0; x < MATRIX_WIDTH; x++) {
@@ -425,9 +440,30 @@ void handle_line_clears(void)
             }
         }
         if (line_full) {
-            clear_line(y);
+            clear_line(y--);
         }
     }
+    phase_time = 0.0;
+    game_phase = GENERATION;
+}
+
+void game_logic(void)
+{
+    phase_time += GetFrameTime();
+    switch (game_phase) {
+        case GENERATION:
+            generation_phase();
+            break;
+        case FALL:
+            fall_phase();
+            break;
+        case LOCK:
+            lock_phase();
+            break;
+        case PATTERN:
+            pattern_phase();
+            break;
+    }
 }
 
 int main(void)
@@ -436,30 +472,23 @@ int main(void)
     InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "~turnipGod's Tetris");
 
 
-    // set all blocks to white
+    // set default block colors
     for (int y = 0; y < 40; y++) {
         for (int x = 0; x < 10; x++) {
-            matrix[x][y] = WHITE;
-        }
-    }
-
-    for (int y = 0; y < 20; y++) {
-        for (int x = 0; x < 10; x++) {
-            matrix[x][y] = BLACK;
+            if (y < 20) {
+                matrix[x][y] = BLACK;
+            } else {
+                matrix[x][y] = WHITE;
+            }
         }
     }
 
-    game_phase = FALL;
     shuffle_bag(piece_queue);
     shuffle_bag(&(piece_queue[7]));
-    generation_phase();
+    phase_time = 0.0;
+    game_phase = GENERATION;
     while (!WindowShouldClose()) {
-        handle_lr();
-        handle_rotate();
-        handle_hold();
-        handle_hard_drop();
-        handle_fall();
-        handle_line_clears();
+        game_logic();
         BeginDrawing();
         ClearBackground(WHITE);
         render_matrix();