From d118ad41559a5137703597d0be86e3eaaf85a92f Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Sat, 26 Oct 2019 16:27:34 -0400 Subject: add das and almost finish sdl ui --- .gitmodules | 3 + frontends/sdl/config.ini | 9 +++ frontends/sdl/meson.build | 4 + frontends/sdl/sdl_ui.c | 119 ++++++++++++++++++++++++------ ini | 1 + lib/cetris.h | 183 +++++++++++++++++++++++++++++++++------------- meson.build | 2 +- 7 files changed, 245 insertions(+), 76 deletions(-) create mode 100644 .gitmodules create mode 100644 frontends/sdl/config.ini create mode 160000 ini diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..98561ca --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ini"] + path = ini + url = https://github.com/Pizzabelly/ini.git diff --git a/frontends/sdl/config.ini b/frontends/sdl/config.ini new file mode 100644 index 0000000..5ca9c34 --- /dev/null +++ b/frontends/sdl/config.ini @@ -0,0 +1,9 @@ +[das] +das = 220 +arr = 15 + +[game] +drop_delay = 20 +next_piece_delay = 40 +lock_delay = 30 +wait_on_clear = 0 diff --git a/frontends/sdl/meson.build b/frontends/sdl/meson.build index 64d70e4..d7e7f44 100644 --- a/frontends/sdl/meson.build +++ b/frontends/sdl/meson.build @@ -4,6 +4,10 @@ threads = dependency('threads') sdl = dependency('SDl2') ttf = dependency('SDL2_ttf') +configure_file(input: 'config.ini', + output: 'config.ini', + copy: true, install: false) + executable('cetris', src, dependencies: [threads, sdl, ttf], include_directories: cetris_inc, diff --git a/frontends/sdl/sdl_ui.c b/frontends/sdl/sdl_ui.c index 0efd9cb..c29fd1d 100644 --- a/frontends/sdl/sdl_ui.c +++ b/frontends/sdl/sdl_ui.c @@ -7,19 +7,24 @@ #include #include #include + #include +#include #define W 480 #define H 640 -#define X_OFFSET 60 -#define Y_OFFSET 30 +#define BLOCK_SIZE 25 + +#define X_OFFSET (W/2) - (BLOCK_SIZE * 5) +#define Y_OFFSET (H/2) - (BLOCK_SIZE * 10) SDL_Renderer* render; TTF_Font* font; cetris_game g; +/* uint8_t colors[8][3] = { {0, 0, 0}, // Black {127,219,255}, // Aqua @@ -30,6 +35,17 @@ uint8_t colors[8][3] = { {0,31,63}, // Navy {255,220,0} // Yellow }; +*/ + +uint8_t colors[7][3] = { + {174,198,207}, // Aqua + {170,221,119}, // Olive + {177,156,217}, // Purple + {255,209,220}, // Fuchsia + {255,179,71}, // Orange + {119,158,203}, // Navy + {253,253,150} // Yellow +}; #ifdef _WIN32 DWORD WINAPI game_loop(void* data) { @@ -56,6 +72,7 @@ void setup() { render = SDL_CreateRenderer(win, -1, SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_ACCELERATED); SDL_RenderSetLogicalSize(render, W, H); if (!render) exit(fprintf(stderr, "err: could not create SDL renderer\n")); + SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND); //TTF_Init(); //font = TTF_OpenFont("LiberationMono-Regular.ttf", 18); @@ -66,36 +83,47 @@ void draw() { SDL_RenderClear(render); SDL_Rect board = {X_OFFSET, Y_OFFSET, 250, 500}; - SDL_SetRenderDrawColor(render, 240, 240, 240, 255); + SDL_SetRenderDrawColor(render, 235, 235, 235, 255); SDL_RenderFillRect(render, &board); SDL_RenderDrawRect(render, &board); SDL_SetRenderDrawColor(render, 255, 255, 255, 255); for (int x = 0; x < CETRIS_BOARD_X + 1; x++) { - int rx = X_OFFSET + 1 + (x * 25); + int rx = X_OFFSET + 1 + (x * BLOCK_SIZE); SDL_RenderDrawLine(render, rx, Y_OFFSET + 1, rx, Y_OFFSET + 500); } for (int y = 0; y < CETRIS_BOARD_Y - CETRIS_BOARD_VISABLE + 1; y++) { - int ry = Y_OFFSET + (y * 25); + int ry = Y_OFFSET + (y * BLOCK_SIZE); SDL_RenderDrawLine(render, X_OFFSET + 1, ry, X_OFFSET + 250, ry); } - SDL_Rect b = {0, 0, 25, 25}; + SDL_Rect b = {0, 0, BLOCK_SIZE - 1, BLOCK_SIZE - 1}; + SDL_Rect w = {0, 0, BLOCK_SIZE + 1, BLOCK_SIZE + 1}; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { if ((g.current.m[y]>>(3 - x))&1) { - b.x = X_OFFSET + ((x + g.current.pos.x) * 25); - b.y = (Y_OFFSET + ((y + g.current.pos.y) * 25)) - (CETRIS_BOARD_VISABLE * 25); - + b.x = X_OFFSET + ((x + g.current.pos.x) * BLOCK_SIZE); + b.y = (Y_OFFSET + ((y + g.current.pos.y) * BLOCK_SIZE)) - (CETRIS_BOARD_VISABLE * BLOCK_SIZE); + uint8_t (*color)[3] = &(colors[g.current.t]); SDL_SetRenderDrawColor(render, (*color)[0], (*color)[1], (*color)[2], 255); - + + SDL_RenderFillRect(render, &b); SDL_RenderDrawRect(render, &b); - b.y = (Y_OFFSET + ((y + g.current.ghost_y) * 25)) - (CETRIS_BOARD_VISABLE * 25); + SDL_SetRenderDrawColor(render, 240, 240, 240, 255); + w.y = b.y - 1; + w.x = b.x - 1; + SDL_RenderDrawRect(render, &w); + + b.y = (Y_OFFSET + ((y + g.current.ghost_y) * BLOCK_SIZE)) - (CETRIS_BOARD_VISABLE * BLOCK_SIZE); + + w.y = b.y - 1; + SDL_RenderDrawRect(render, &w); - SDL_SetRenderDrawColor(render, 0, 0, 0, 255); + SDL_SetRenderDrawColor(render, (*color)[0], (*color)[1], (*color)[2], 180); + SDL_RenderFillRect(render, &b); SDL_RenderDrawRect(render, &b); } @@ -108,22 +136,29 @@ void draw() { for (int x = 0; x < CETRIS_BOARD_X; x++) { for (int y = g.highest_line; y < CETRIS_BOARD_Y; y++) { if (g.board[x][y] & SLOT_OCCUPIED) { - b.x = X_OFFSET + (x * 25); - b.y = (Y_OFFSET + (y * 25)) - (CETRIS_BOARD_VISABLE * 25); + b.x = X_OFFSET + (x * BLOCK_SIZE); + b.y = (Y_OFFSET + (y * BLOCK_SIZE)) - (CETRIS_BOARD_VISABLE * BLOCK_SIZE); - uint8_t (*color)[3] = &(colors[(g.board[x][y] >> 5)]); - SDL_SetRenderDrawColor(render, (*color)[0], (*color)[1], (*color)[2], 255); - if (g.line_remove_tick[y]) { - if (g.tick % 2 == 0) { - SDL_RenderDrawRect(render, &b); + if ((int)g.tick % 2 == 0) { + continue; } - } else { - SDL_RenderDrawRect(render, &b); } + + SDL_SetRenderDrawColor(render, 240, 240, 240, 255); + w.y = b.y - 1; + w.x = b.x - 1; + SDL_RenderDrawRect(render, &w); + + uint8_t (*color)[3] = &(colors[(g.board[x][y] >> 5)]); + SDL_SetRenderDrawColor(render, (*color)[0], (*color)[1], (*color)[2], 255); + SDL_RenderFillRect(render, &b); + + SDL_RenderDrawRect(render, &b); } } } + SDL_RenderPresent(render); } @@ -131,6 +166,20 @@ int main(void) { setup(); init_game(&g); + + ini_parser p; + load_ini_file(&p, "config.ini"); + + int das = atoi(get_ini_value(&p, "das", "das")); + g.config.das_das = das * .06f; + + int arr = atoi(get_ini_value(&p, "das", "arr")); + g.config.das_arr = arr * .06f; + + g.config.drop_period = atoi(get_ini_value(&p, "game", "drop_delay")) * .06f; + g.config.next_piece_delay = atoi(get_ini_value(&p, "game", "next_piece_delay")); + g.config.lock_delay = atoi(get_ini_value(&p, "game", "lock_delay")); + g.config.wait_on_clear = atoi(get_ini_value(&p, "game", "wait_on_clear")); #ifdef _WIN32 HANDLE thread = CreateThread(NULL, 0, game_loop, NULL, 0, NULL); @@ -152,16 +201,40 @@ int main(void) { case SDLK_RIGHT: move_piece(&g, RIGHT); break; case SDLK_DOWN: - move_piece(&g, DOWN); break; + move_piece(&g, USER_DOWN); break; case SDLK_SPACE: move_piece(&g, HARD_DROP); break; case SDLK_UP: move_piece(&g, ROTATE_CW); break; + case 'c': + hold_piece(&g); break; + case 'x': + move_piece(&g, ROTATE_CW); break; + case 'z': + move_piece(&g, ROTATE_CCW); break; + } + break; + case SDL_KEYUP: + switch (e.key.keysym.sym) { + case SDLK_LEFT: + unhold_piece(&g, LEFT); break; + case SDLK_RIGHT: + unhold_piece(&g, RIGHT); break; + case SDLK_DOWN: + unhold_piece(&g, USER_DOWN); break; + case SDLK_SPACE: + unhold_piece(&g, HARD_DROP); break; + case SDLK_UP: + unhold_piece(&g, ROTATE_CW); break; + case 'x': + unhold_piece(&g, ROTATE_CW); break; + case 'z': + unhold_piece(&g, ROTATE_CCW); break; } } } draw(); - SDL_Delay(1000 / 60); + SDL_Delay(1000 / 144); } } diff --git a/ini b/ini new file mode 160000 index 0000000..3401f2e --- /dev/null +++ b/ini @@ -0,0 +1 @@ +Subproject commit 3401f2ed3153c6093c2f434e205cf6e7775a02d5 diff --git a/lib/cetris.h b/lib/cetris.h index 0ccbb9f..8b804a1 100644 --- a/lib/cetris.h +++ b/lib/cetris.h @@ -18,7 +18,18 @@ #define CETRIS_BOARD_Y 40 #define CETRIS_BOARD_VISABLE 20 +#define CETRIS_HI_RES 1 + +#if CETRIS_HI_RES +#define CETRIS_HZ 6000 +typedef float ctick; +#else #define CETRIS_HZ 60 +typedef uint16_t ctick; +#endif + +#define CETRIS_ENABLE_DAS 1 + #define CETRIS_DROP_PERIOD 2 #define CETRIS_NEXT_PIECE_DELAY 40 #define CETRIS_LINE_CLEAR_DELAY 40 @@ -77,37 +88,61 @@ typedef enum { } input_t; typedef struct { - /* playfield represented by a 2d array */ +#if CETRIS_ENABLE_DAS + ctick das_arr; + ctick das_das; +#endif + + ctick drop_period; + ctick next_piece_delay; + ctick line_delay_clear; + ctick lock_delay; + + bool wait_on_clear; +} cetris_config; + +typedef struct { + // playfield represented by a 2d array uint8_t board[CETRIS_BOARD_X][CETRIS_BOARD_Y]; int8_t highest_line; - /* constant queue of all 7 possible tetrimino */ + // constant queue of all 7 possible tetrimino uint8_t piece_queue[7]; - /* current tetrimino */ + // current tetrimino tetrimino current; tetrimino held; bool piece_held; uint8_t current_index; - /* internal game tick */ - uint16_t tick; - uint16_t next_drop_tick; - uint16_t next_piece_tick; - uint16_t down_move_tick; - uint16_t line_remove_tick[CETRIS_BOARD_Y]; + // internal game tick + ctick tick; + ctick next_drop_tick; + ctick next_piece_tick; + ctick down_move_tick; + ctick line_remove_tick[CETRIS_BOARD_Y]; + +#if CETRIS_ENABLE_DAS + ctick das_wait; + ctick next_das_move; + uint8_t das_move; + uint8_t held_moves[8]; +#endif - /* progress trackers */ + // progress trackers uint8_t lines; uint8_t level; bool game_over; - /* scoring flags */ + // scoring flags bool tspin; bool mini_tspin; - /* score counter */ + // score counter uint16_t score; + + // config + cetris_config config; } cetris_game; const piece_matrix default_matrices[7] = { @@ -211,25 +246,6 @@ static int check_matrix(cetris_game *g, piece_matrix *m) { return 1; } -static void set_matrix(cetris_game *g, piece_matrix *m) { - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - if (((*m)[y]>>(3 - x))&1) { - vec2 r = (vec2){x + g->current.pos.x, y + g->current.pos.y}; - if (r.y >= 0) { - if (!(g->board[r.x][r.y] & SLOT_OCCUPIED)) { - g->board[r.x][r.y] |= SLOT_OCCUPIED; - g->board[r.x][r.y] |= g->current.t << 5; - } - } - if (g->current.ghost_y + y >= 0) - if (r.y != (g->current.ghost_y + y)) - g->board[r.x][g->current.ghost_y + y] |= SLOT_GHOST; - } - } - } -} - // TODO: hard score static void add_score(cetris_game *g, int lines) { if (!g->tspin && !g->mini_tspin) { @@ -292,10 +308,14 @@ static void make_ghosts(cetris_game *g) { g->current.pos.y = orig_y; } -static void move_current(cetris_game *g, input_t move) { +static void move_current(cetris_game *g, uint8_t move) { if (g->game_over || g->next_piece_tick) return; +#if CETRIS_ENABLE_DAS + if (move == USER_DOWN) return; +#endif + g->current.pos.y += basic_movements[move].y; g->current.pos.x += basic_movements[move].x; @@ -305,7 +325,7 @@ static void move_current(cetris_game *g, input_t move) { g->current.pos.x -= basic_movements[move].x; if (move == DOWN && check == -1 && !g->current.lock_tick) { - g->current.lock_tick = g->tick + CETRIS_LOCK_DELAY; + g->current.lock_tick = g->tick + g->config.lock_delay; } } else { if (move == USER_DOWN) @@ -477,29 +497,37 @@ void update_board(cetris_game *g) { clear_line = false; } } - // remove tick only tracked on first block of line - if (g->line_remove_tick[y] && g->line_remove_tick[y] <= g->tick) { - g->line_remove_tick[y] = 0; + + if (g->config.wait_on_clear) { + // remove tick only tracked on first block of line + if (g->line_remove_tick[y] && g->line_remove_tick[y] <= g->tick) { + g->line_remove_tick[y] = 0; + for (int s = y - 1; s >= 0; s--) { + for (int x = 0; x < CETRIS_BOARD_X; x++) { + g->board[x][s + 1] = g->board[x][s]; + } + } + } + if (clear_line) { + g->line_remove_tick[y] = g->tick + g->config.line_delay_clear; + lines_cleared++; + } + } else if (clear_line) { for (int s = y - 1; s >= 0; s--) { for (int x = 0; x < CETRIS_BOARD_X; x++) { g->board[x][s + 1] = g->board[x][s]; } } } - if (clear_line) { - g->line_remove_tick[y] = g->tick + CETRIS_LINE_CLEAR_DELAY; - lines_cleared++; - } } make_ghosts(g); - //set_matrix(g, &g->current.m); assert(lines_cleared <= 4); if (g->current.locked && !g->next_piece_tick) { if (lines_cleared > 0) { - g->next_piece_tick = g->tick + CETRIS_NEXT_PIECE_DELAY; + g->next_piece_tick = g->tick + g->config.next_piece_delay; } else { next_piece(g); } @@ -528,7 +556,39 @@ CETRIS_EXPORT void hold_piece(cetris_game *g) { update_board(g); } -CETRIS_EXPORT void move_piece(cetris_game *g, input_t move) { +#if CETRIS_ENABLE_DAS +CETRIS_EXPORT void unhold_piece(cetris_game* g, input_t move) { + if (g->das_move == move) { + if (move == LEFT && g->held_moves[RIGHT]) { + g->das_move = RIGHT; + g->das_wait = g->tick + g->config.das_das; + } else if (move == RIGHT && g->held_moves[LEFT]) { + g->das_move = LEFT; + g->das_wait = g->tick + g->config.das_das; + } else { + g->das_wait = 0; + } + g->next_das_move = 0; + } + g->held_moves[move] = 0; +} +#endif + +CETRIS_EXPORT void move_piece(cetris_game *g, uint8_t move) { +#if CETRIS_ENABLE_DAS + if (g->held_moves[move]) return; + if (move == LEFT || move == RIGHT) { + if ((move != g->das_move) || !g->das_wait) { + g->das_move = move; + g->das_wait = g->tick + g->config.das_das; + g->next_das_move = 0; + } + } + if (move == USER_DOWN) + g->next_drop_tick = g->tick + g->config.drop_period; + g->held_moves[move] = 1; +#endif + switch (move) { case LEFT: case RIGHT: @@ -546,6 +606,7 @@ CETRIS_EXPORT void move_piece(cetris_game *g, input_t move) { rotate_piece(g, 0); break; } + } CETRIS_EXPORT void init_game(cetris_game *g) { @@ -573,8 +634,12 @@ CETRIS_EXPORT void init_game(cetris_game *g) { CETRIS_EXPORT void update_game_tick(cetris_game *g) { if (g->game_over) return; - + +#if CETRIS_HI_RES + g->tick += .01f; +#else g->tick++; +#endif if (g->next_piece_tick && g->tick >= g->next_piece_tick) { next_piece(g); @@ -584,16 +649,21 @@ CETRIS_EXPORT void update_game_tick(cetris_game *g) { return; bool did_move = false; - if (g->tick >= g->next_drop_tick || !g->next_drop_tick) { - if (g->next_drop_tick) { - move_current(g, DOWN); - did_move = true; - } + if (g->next_drop_tick && g->tick >= g->next_drop_tick) { + move_current(g, DOWN); + g->next_drop_tick = 0; + did_move = true; + } - if (g->level <= 20) { - g->next_drop_tick = g->tick + level_drop_delay[g->level - 1]; + if (!g->next_drop_tick) { + if (g->held_moves[USER_DOWN]) { + g->next_drop_tick = g->tick + g->config.drop_period; } else { - g->next_drop_tick = g->tick + level_drop_delay[19]; + if (g->level <= 20) { + g->next_drop_tick = g->tick + level_drop_delay[g->level - 1]; + } else { + g->next_drop_tick = g->tick + level_drop_delay[19]; + } } } @@ -610,6 +680,15 @@ CETRIS_EXPORT void update_game_tick(cetris_game *g) { g->current.lock_tick = 0; } +#if CETRIS_ENABLE_DAS + if (g->next_das_move && g->tick >= g->next_das_move) { + g->next_das_move = g->tick + g->config.das_arr; + move_current(g, g->das_move); + } else if (!g->next_das_move && g->das_wait && g->tick >= g->das_wait) { + g->next_das_move = g->tick + g->config.das_arr; + } +#endif + if (did_move) update_board(g); } diff --git a/meson.build b/meson.build index 91577b5..3cee4ae 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,7 @@ project('cetris', 'c', compiler = meson.get_compiler('c') -cetris_inc = include_directories('lib') +cetris_inc = include_directories(['lib', 'ini']) if get_option('debug') == true if host_machine.system() == 'windows' -- cgit v1.2.3-101-g0448