summaryrefslogtreecommitdiff
path: root/frontends/curses/curses_ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/curses/curses_ui.c')
-rw-r--r--frontends/curses/curses_ui.c258
1 files changed, 258 insertions, 0 deletions
diff --git a/frontends/curses/curses_ui.c b/frontends/curses/curses_ui.c
new file mode 100644
index 0000000..91792f5
--- /dev/null
+++ b/frontends/curses/curses_ui.c
@@ -0,0 +1,258 @@
+/* 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#ifdef _WIN32
+#include <curses.h>
+#include <windows.h>
+#else
+#include <ncurses.h>
+#include <unistd.h>
+#include <pthread.h>
+#endif
+#include <locale.h>
+
+#define CETRIS_HI_RES 1
+#define CETRIS_ENABLE_DAS 0
+#include <cetris.h>
+#include <rules.h>
+
+#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;
+}
+