/* very simple curses interface, DAS is not functional because you cant detect * key up presses on the terminal, there are 2 character sets, ASCII_COMPATIBLE * for no UTF-8 chars and the normal which will look better on a platform that can * support UTF-8. Color support is limited because I havent figured out colors yet */ #include #include #include #include #ifdef _WIN32 #include #include #else #include #include #include #endif #include #define CETRIS_HI_RES 1 #define CETRIS_ENABLE_DAS 0 #include #include #include "../timer.c" #define WIN_COL 55 #define WIN_LINE 25 //#define ASCII_COMPATIBLE #ifdef ASCII_COMPATIBLE #define BLOCK "[]" #define PLAY_FIELD_STR " /--------------------\\ /----------------\\ \n"\ " | | | | \n"\ " | | \\----------------/ \n"\ " | | \n"\ " | | \n"\ " | | /---------\\ \n"\ " | | | | \n"\ " | | | | \n"\ " | | | | \n"\ " | | | | \n"\ " | | \\---------/ \n"\ " | | \n"\ " | | \n"\ " | | \n"\ " | | \n"\ " | | \n"\ " | | \n"\ " | | \n"\ " | | \n"\ " | | \n"\ " | | \n"\ " \\--------------------/" #else #define BLOCK "[]" #define PLAY_FIELD_STR " ┏━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━score━━━━━┓ \n"\ " ┃ ┃ ┃ ┃ \n"\ " ┃ ┃ ┗━━━━━━━━━━━━━━━┛ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ ┏━━━queue━━━┓ \n"\ " ┃ ┃ ┃ ┃ \n"\ " ┃ ┃ ┃ ┃ \n"\ " ┃ ┃ ┃ ┃ \n"\ " ┃ ┃ ┃ ┃ \n"\ " ┃ ┃ ┗━━━━━━━━━━━┛ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┗━━━━━━━━━━━━━━━━━━━━┛ " #endif #define X_OFFSET 8 #define Y_OFFSET -2 ctrs_game game = { 0 }; bool is_paused = false; void curses_init() { setlocale(LC_CTYPE, ""); initscr(); curs_set(0); noecho(); keypad(stdscr, TRUE); timeout(2); #ifdef _WIN32 // only resize manually on windows resize_term(WIN_LINE, WIN_COL); #endif start_color(); init_pair(MINO_O, COLOR_YELLOW, COLOR_BLACK); init_pair(MINO_Z, COLOR_RED, COLOR_BLACK); init_pair(MINO_S, COLOR_GREEN, COLOR_BLACK); init_pair(MINO_T, COLOR_MAGENTA, COLOR_BLACK); init_pair(MINO_L, COLOR_WHITE, COLOR_BLACK); // should be orange init_pair(MINO_I, COLOR_CYAN, COLOR_BLACK); init_pair(MINO_J, COLOR_BLUE, COLOR_BLACK); clear(); } #ifdef _WIN32 DWORD WINAPI game_loop(void* data) { while(1) { Sleep((1.0/60.0) * 1000.0); if (!is_paused) ctrs_update_game_tick(&game); } return 0; } #else void *game_loop(void) { struct timespec start_time, end_time; clock_gettime(CLOCK_MONOTONIC_RAW, &start_time); while (1) { clock_gettime(CLOCK_MONOTONIC_RAW, &end_time); long nsec_elapsed = (end_time.tv_sec - start_time.tv_sec) * (long)1e9 + (end_time.tv_nsec - start_time.tv_nsec); game.timer = nsec_elapsed / 1000; game.tick = nsec_elapsed / 1000000; if (!ctrs_update_game_tick(&game)) { break; } usleep(1000); } return 0; } #endif void draw_board() { mvaddstr(0, 0, PLAY_FIELD_STR); for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { if ((game.current.m[y]>>(3 - x))&1) { int draw_x = X_OFFSET + ((x + game.current.pos.x) * 2); int draw_y = Y_OFFSET + (y + game.current.pos.y) - game.config.board_visible; int ghost_y = Y_OFFSET + (y + game.current.ghost_y) - game.config.board_visible; attron(A_DIM); mvaddstr(ghost_y, draw_x, BLOCK); attroff(A_DIM); attron(COLOR_PAIR(game.current.t) | A_BOLD); mvaddstr(draw_y, draw_x, BLOCK); attroff(COLOR_PAIR(game.current.t) | A_BOLD); } if ((ctrs_default_matrices[game.piece_queue[game.current_index]][y]>>(3 - x))&1) { mvaddstr(6 + y, (x * 2) + 36, BLOCK); } } } for (int x = 0; x < game.config.board_x; x++) { for (int y = game.highest_line; y < game.config.board_y; y++) { int draw_y = y - game.config.board_visible + Y_OFFSET; int draw_x = x * 2 + X_OFFSET; if (game.board[x][y] & CTRS_SLOT_OCCUPIED) { attron(COLOR_PAIR(game.board[x][y] >> 5) | A_BOLD); if (game.line_remove_tick[y]) { if (game.tick % 2 == 0) { mvaddstr(draw_y, draw_x, BLOCK); } } else { mvaddstr(draw_y, draw_x, BLOCK); } attroff(COLOR_PAIR(game.board[x][y] >> 5) | A_BOLD); } } } attron(A_BOLD); char score[50]; sprintf(score, "%i", game.score); mvaddstr(1, (39 + X_OFFSET) - strlen(score), score); char level[20]; sprintf(level, "%i", game.level); mvaddstr(3, 37, "Level"); mvaddstr(4, 40 - strlen(level), level); if (game.game_over) { mvaddstr(10, 5 + X_OFFSET, "GAME OVER"); mvaddstr(11, 4 + X_OFFSET, "r to restart"); } if (is_paused) { mvaddstr(10, 7 + X_OFFSET, "paused"); } attroff(A_BOLD); } int main(void) { curses_init(); game.config = tetris_ds_config; game.config.levels = &tetris_worlds_levels[0]; game.config.win_condition = twenty_line_sprint; game.config.wait_on_clear = 0; ctrs_init_game(&game); ctrs_start_game(&game); /* #ifdef _WIN32 HANDLE thread = CreateThread(NULL, 0, game_loop, NULL, 0, NULL); #else pthread_t thread; pthread_create(&thread, NULL, (void*)game_loop, (void*)0); #endif */ int c; while(1) { c = getch(); switch (c) { case 'q': endwin(); exit(1); case 27: // esc or alt is_paused = !is_paused; break; case 'r': if (game.game_over) { ctrs_init_game(&game); } break; } if (is_paused) continue; // dont allow input if paused switch (c) { case KEY_LEFT: ctrs_move_piece(&game, LEFT); break; case KEY_RIGHT: ctrs_move_piece(&game, RIGHT); break; case KEY_DOWN: ctrs_move_piece(&game, DOWN); break; case KEY_UP: case 'x': ctrs_move_piece(&game, ROTATE_CW); break; case '^': case 'z': ctrs_move_piece(&game, ROTATE_CCW); break; case ' ': ctrs_move_piece(&game, HARD_DROP); break; case KEY_SLEFT: case 'c': ctrs_hold_piece(&game); break; } erase(); draw_board(); refresh(); } return 0; }