summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--inc/tetromino.h86
-rw-r--r--src/main.c125
2 files changed, 184 insertions, 27 deletions
diff --git a/inc/tetromino.h b/inc/tetromino.h
index 30d55a3..04f21d3 100644
--- a/inc/tetromino.h
+++ b/inc/tetromino.h
@@ -20,6 +20,8 @@ struct Tetromino {
     int size;
     struct Color color;
     int directions[4][4][4];
+    int cw_kick_table[4][10];
+    int ccw_kick_table[4][10];
 };
 
 struct Tetromino tetrominos[7] = {
@@ -52,6 +54,18 @@ struct Tetromino tetrominos[7] = {
                 {0, 1, 0, 0},
                 {0, 1, 0, 0}
             }
+        },
+        .cw_kick_table = {
+            {+0,+0, -2,+0, +1,+0, -2,-1, +1,+2},
+            {+0,+0, -1,+0, +2,+0, -1,+2, +2,-1},
+            {+0,+0, +2,+0, -1,+0, +2,+1, -1,-2},
+            {+0,+0, +1,+0, -2,+0, +1,-2, -2,+1}
+        },
+        .ccw_kick_table = {
+            {+0,+0, -1,+0, +2,+0, -1,+2, +2,-1},
+            {+0,+0, +2,+0, -1,+0, +2,+1, -1,-2},
+            {+0,+0, +1,+0, -2,+0, +1,-2, -2,+1},
+            {+0,+0, -2,+0, +1,+0, -2,-1, +1,+2}
         }
     },
     [J] = {
@@ -78,6 +92,18 @@ struct Tetromino tetrominos[7] = {
                 {0, 1, 0},
                 {0, 1, 0}
             }
+        },
+        .cw_kick_table = {
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
+        },
+        .ccw_kick_table = {
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
         }
     },
     [L] = {
@@ -104,6 +130,18 @@ struct Tetromino tetrominos[7] = {
                 {0, 1, 0},
                 {1, 1, 0}
             }
+        },
+        .cw_kick_table = {
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
+        },
+        .ccw_kick_table = {
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
         }
     },
     [O] = {
@@ -130,6 +168,18 @@ struct Tetromino tetrominos[7] = {
                 {0, 1, 1},
                 {0, 1, 1}
             }
+        },
+        .cw_kick_table = {
+            {+0,+0, +0,+0, +0,+0, +0,+0, +0,+0},
+            {+0,+0, +0,+0, +0,+0, +0,+0, +0,+0},
+            {+0,+0, +0,+0, +0,+0, +0,+0, +0,+0},
+            {+0,+0, +0,+0, +0,+0, +0,+0, +0,+0}
+        },
+        .ccw_kick_table = {
+            {+0,+0, +0,+0, +0,+0, +0,+0, +0,+0},
+            {+0,+0, +0,+0, +0,+0, +0,+0, +0,+0},
+            {+0,+0, +0,+0, +0,+0, +0,+0, +0,+0},
+            {+0,+0, +0,+0, +0,+0, +0,+0, +0,+0}
         }
     },
     [S] = {
@@ -156,6 +206,18 @@ struct Tetromino tetrominos[7] = {
                 {1, 1, 0},
                 {1, 0, 0}
             }
+        },
+        .cw_kick_table = {
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
+        },
+        .ccw_kick_table = {
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
         }
     },
     [T] = {
@@ -182,6 +244,18 @@ struct Tetromino tetrominos[7] = {
                 {1, 1, 0},
                 {0, 1, 0}
             }
+        },
+        .cw_kick_table = {
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
+        },
+        .ccw_kick_table = {
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
         }
     },
     [Z] = {
@@ -208,6 +282,18 @@ struct Tetromino tetrominos[7] = {
                 {1, 1, 0},
                 {0, 1, 0}
             }
+        },
+        .cw_kick_table = {
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
+        },
+        .ccw_kick_table = {
+            {+0,+0, +1,+0, +1,+1, +0,-2, +1,-2},
+            {+0,+0, +1,+0, +1,-1, +0,+2, +1,+2},
+            {+0,+0, -1,+0, -1,+1, +0,-2, -1,-2},
+            {+0,+0, -1,+0, -1,-1, +0,+2, -1,+2}
         }
     }
 };
diff --git a/src/main.c b/src/main.c
index 82e9e90..9ef03f0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -12,6 +12,8 @@
 #define BLOCK_SIZE 25
 
 #define FALL_TICK 0.2
+#define SOFT_DROP_MULT 4
+#define LOCK_TICK 0.5
 
 enum Phases {
     GENERATION,
@@ -40,12 +42,32 @@ int current_bag = 0;
 int current_piece_index = 0;
 int hold = 7;
 int held = 0;
+int hard_drop = 0;
+double phase_time = 0.0;
 
 void draw_block(int x, int y, int width, int height, struct Color color)
 {
     DrawRectangle(x, -y + SCREEN_HEIGHT - height, width, height, color);
 }
 
+int is_overlap(int x, int 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) {
+                if (current_piece.pos_x + i - 1 + x >= MATRIX_WIDTH
+                        || current_piece.pos_x + i - 1 + x < 0
+                        || current_piece.pos_y + j - 1 + y < 0
+                        || (!ColorIsEqual(BLACK, matrix[current_piece.pos_x + i - 1 + x][current_piece.pos_y + j - 1 + y])
+                         && !ColorIsEqual(WHITE, matrix[current_piece.pos_x + i - 1 + x][current_piece.pos_y + j - 1 + y]))) {
+                    return 1;
+                }
+            }
+        }
+    }
+    return 0;
+}
+
 void render_matrix(void)
 {
     int matrix_origin_x = (SCREEN_WIDTH/2 - (MATRIX_WIDTH * BLOCK_SIZE))/2;
@@ -73,6 +95,33 @@ void render_matrix(void)
         }
     }
 
+    /* draw ghost piece */
+    int ghost_y = 0;
+    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
+                );
+            }
+        }
+    }
+
     /* 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++) {
@@ -152,24 +201,6 @@ void shuffle_bag(int *bag)
     }
 }
 
-int is_overlap(int x, int 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) {
-                if (current_piece.pos_x + i - 1 + x >= MATRIX_WIDTH
-                        || current_piece.pos_x + i - 1 + x < 0
-                        || current_piece.pos_y + j - 1 + y < 0
-                        || (!ColorIsEqual(BLACK, matrix[current_piece.pos_x + i - 1 + x][current_piece.pos_y + j - 1 + y])
-                         && !ColorIsEqual(WHITE, matrix[current_piece.pos_x + i - 1 + x][current_piece.pos_y + j - 1 + y]))) {
-                    return 1;
-                }
-            }
-        }
-    }
-    return 0;
-}
-
 void generation_phase(void)
 {
     // bag generation
@@ -204,6 +235,7 @@ void handle_hold(void)
             current_piece.type = hold;
             hold = temp;
         }
+        game_phase = FALL;
         current_piece.rotation = NORTH;
         current_piece.pos_x = 4;
         current_piece.pos_y = 20;
@@ -219,10 +251,24 @@ 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;
-            if (is_overlap(0, 0)) {
+            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;
             }
@@ -232,9 +278,20 @@ void handle_rotate(void)
             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;
-            if (is_overlap(0, 0)) {
+            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;
             }
@@ -251,6 +308,9 @@ void move_right(void)
     if (is_overlap(1, 0)) {
         return;
     }
+    if (game_phase == LOCK) {
+        phase_time = 0.0;
+    }
     current_piece.pos_x++;
 }
 
@@ -259,6 +319,9 @@ void move_left(void)
     if (is_overlap(-1, 0)) {
         return;
     }
+    if (game_phase == LOCK) {
+        phase_time = 0.0;
+    }
     current_piece.pos_x--;
 }
 
@@ -297,10 +360,9 @@ void move_piece_to_matrix(void)
 
 void handle_fall(void)
 {
-    static double phase_time = 0.0;
+    phase_time += GetFrameTime();
     if (game_phase == FALL) {
-        phase_time += GetFrameTime();
-        if ((!IsKeyDown(KEY_S) && phase_time < FALL_TICK) || phase_time < FALL_TICK * 0.5) {
+        if (!hard_drop && ((!IsKeyDown(KEY_S) && phase_time < FALL_TICK) || phase_time < FALL_TICK / SOFT_DROP_MULT)) {
             return;
         } else {
             phase_time = 0.0;
@@ -308,21 +370,30 @@ void handle_fall(void)
     }
 
     if (is_overlap(0, -1)) {
+        game_phase = LOCK;
+    } else {
+        game_phase = FALL;
+        current_piece.pos_y--;
+    }
+
+    if (is_overlap(0, -1) && (phase_time >= LOCK_TICK || hard_drop)) {
         move_piece_to_matrix();
         generation_phase();
         held = 0;
+        game_phase = FALL;
+        phase_time = 0.0;
+        hard_drop = 0;
         return;
-    }
-
-    current_piece.pos_y--;
+    } 
 }
 
 void handle_hard_drop(void)
 {
     static int dropped = 0;
-    if (game_phase == FALL) {
+    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--;
             }