diff options
| author | 2019-10-30 13:32:36 -0400 | |
|---|---|---|
| committer | 2019-10-30 13:32:36 -0400 | |
| commit | 55f3c07f153b4a9c8e70801fec4e3f1c603ba78d (patch) | |
| tree | 1c0f63e1bb49a39f321e2ec52c5b5cb1facdf3fe | |
| parent | 93902c57123c8e33f99263474eac726d4dbdceea (diff) | |
| download | cetris-55f3c07f153b4a9c8e70801fec4e3f1c603ba78d.tar.gz cetris-55f3c07f153b4a9c8e70801fec4e3f1c603ba78d.tar.bz2 cetris-55f3c07f153b4a9c8e70801fec4e3f1c603ba78d.zip | |
sdl ui refactor
| -rw-r--r-- | 55c76847caa311c7caad17a66beb31a.jpg-autosave.kra | bin | 2066257 -> 0 bytes | |||
| -rw-r--r-- | frontends/sdl/cetris_sdl.c | 546 | ||||
| -rw-r--r-- | frontends/sdl/cetris_sdl.h | 53 | ||||
| -rw-r--r-- | frontends/sdl/meson.build | 2 | ||||
| -rw-r--r-- | frontends/sdl/sdl_ui.c | 517 | ||||
| -rw-r--r-- | lib/cetris.c | 16 | ||||
| -rw-r--r-- | lib/cetris.h | 5 | ||||
| -rw-r--r-- | lib/rules.h | 5 | ||||
| -rw-r--r-- | lib/timer.h | 5 |
9 files changed, 616 insertions, 533 deletions
diff --git a/55c76847caa311c7caad17a66beb31a.jpg-autosave.kra b/55c76847caa311c7caad17a66beb31a.jpg-autosave.kra Binary files differdeleted file mode 100644 index 9992e99..0000000 --- a/55c76847caa311c7caad17a66beb31a.jpg-autosave.kra +++ /dev/null diff --git a/frontends/sdl/cetris_sdl.c b/frontends/sdl/cetris_sdl.c new file mode 100644 index 0000000..66d4667 --- /dev/null +++ b/frontends/sdl/cetris_sdl.c @@ -0,0 +1,546 @@ +#define SDL_MAIN_HANDLED +#ifdef _WIN32 +#include <SDL.h> +#include <SDL_ttf.h> +#include <SDL_image.h> +#define format_str sprintf_s +#else +#include <SDL2/SDL.h> +#include <SDL2/SDL_ttf.h> +#include <SDL2/SDL_image.h> +#define format_str snprintf +#endif +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <time.h> + +#include <cetris.h> +#include <timer.h> +#include <rules.h> +#include <ini.h> + +#include "cetris_sdl.h" + +#define W 900 +#define H 720 +#define FRAME_RATE 60 + +void setup_sdl(cetris_ui *ui) { + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO); + ui->window = SDL_CreateWindow("cetris", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, W, H, SDL_WINDOW_SHOWN); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + SDL_SetHint(SDL_HINT_RENDER_DRIVER,"opengl"); + ui->render = SDL_CreateRenderer(ui->window, -1, SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_ACCELERATED); + SDL_SetRenderDrawBlendMode(ui->render, SDL_BLENDMODE_BLEND); + SDL_RenderSetLogicalSize(ui->render, W, H); + if (!ui->render) exit(fprintf(stderr, "err: could not create SDL renderer\n")); + + TTF_Init(); + ui->font_count = 0; + + IMG_Init(IMG_INIT_PNG); + + ui->skin.clear_sound = (audio_clip_t *)malloc(sizeof(audio_clip_t) * ui->skin.clear_sound_count); + for (int i = 0; i < ui->skin.clear_sound_count; i++) { + char name[25]; + format_str(name, 25, "data/bclear_%i.wav", i); + + SDL_LoadWAV(name, &ui->skin.clear_sound[i].wav_spec, + &ui->skin.clear_sound[i].wav_buffer, &ui->skin.clear_sound[i].wav_length); + } + + SDL_LoadWAV("data/lock.wav", &ui->skin.lock_sound.wav_spec, + &ui->skin.lock_sound.wav_buffer, &ui->skin.lock_sound.wav_length); + + SDL_LoadWAV("data/tetris.wav", &ui->skin.tetris_sound.wav_spec, + &ui->skin.tetris_sound.wav_buffer, &ui->skin.tetris_sound.wav_length); + + ui->audio_device = SDL_OpenAudioDevice(NULL, 0, &ui->skin.clear_sound[0].wav_spec, NULL, 0); + if (ui->audio_device == 0) printf("failed to open audio device\n"); +} + +TTF_Font* get_font(cetris_ui *ui, int size) { + for (int i = 0; i < ui->font_count; i++) { + if (ui->fonts[i].size == size) { + return ui->fonts[i].font; + } + } + ui->fonts[ui->font_count].font = TTF_OpenFont("data/OpenSans-Regular.ttf", size); + TTF_SetFontHinting(ui->fonts[ui->font_count].font, TTF_HINTING_MONO); + ui->fonts[ui->font_count].size = size; + return ui->fonts[ui->font_count++].font; +} + +void draw_text(cetris_ui *ui, char* string, int x, int y, TTF_Font* font, SDL_Color color) { + SDL_Surface *surface; + surface = TTF_RenderText_Solid(font, string, color); + + SDL_Rect message; + message.x = x; + message.y = y; + message.w = surface->w; + message.h = surface->h; + SDL_Texture* tex = SDL_CreateTextureFromSurface(ui->render, surface); + + SDL_RenderCopy(ui->render, tex, NULL, &message); + SDL_DestroyTexture(tex); + SDL_FreeSurface(surface); +} + +SDL_Texture* load_png(cetris_ui* ui, char* file) { + SDL_Surface* loaded_surface = IMG_Load(file); + return SDL_CreateTextureFromSurface(ui->render, loaded_surface); +} + +void draw_image(cetris_ui *ui, SDL_Texture *i, int x, int y, int width, int height) { + SDL_Rect dest = {x, y, width, height}; + SDL_RenderCopy(ui->render, i, NULL, &dest); +} + +void draw_block(cetris_ui *ui, int x, int y, int width, int height, SDL_Color c, SDL_Color off) { + SDL_Rect b = {x + 1, y + 1, width - 1, height - 1}; + SDL_SetRenderDrawColor(ui->render, c.r, c.g, c.b, c.a); + SDL_RenderFillRect(ui->render, &b); + SDL_RenderDrawRect(ui->render, &b); + SDL_SetRenderDrawColor(ui->render, off.r, off.g, off.b, off.a); + b.y--; b.x--; b.w+=2; b.h+=2; + SDL_RenderDrawRect(ui->render, &b); +} + +void draw_board(cetris_ui *ui, SDL_Texture *m, game_board_t* board, int x, int y, int w, int h) { + SDL_SetRenderTarget(ui->render, m); + SDL_RenderClear(ui->render); + + SDL_Rect background = {0, 0, w, h + 5}; + + SDL_SetRenderDrawColor(ui->render, + board->scheme.off.r - (int)(fmod((double)board->count_down, (double)1.0) * 50), + board->scheme.off.g, board->scheme.off.b, board->scheme.off.a); + + SDL_RenderFillRect(ui->render, &background); + SDL_RenderDrawRect(ui->render, &background); + + SDL_SetRenderDrawColor(ui->render, board->scheme.main.r, + board->scheme.main.g, board->scheme.main.b, board->scheme.main.a); + + int y_offset = 5; + + int board_x = board->game.config.board_x; + int board_y = board->game.config.board_y; + int board_visible = board_y - board->game.config.board_visible; + + int block_width = w / board->game.config.board_x; + int block_height = h / board->game.config.board_visible; + + for (int s = 0; s < board_x + 1; s++) { + int rx = (s * block_width); + SDL_RenderDrawLine(ui->render, rx, 1, rx, h + 5); + } + + for (int j = 0; j < board->game.config.board_visible + 1; j++) { + int ry = y_offset + (j * block_height); + SDL_RenderDrawLine(ui->render, 0, ry, w, ry); + } + + for (int s = board->game.highest_line; s < board_y; s++) { + for (int j = 0; j < board_x; j++) { + if (board->game.board[j][s] & SLOT_OCCUPIED) { + if (board->game.line_remove_tick[s]) { + if (board->game.tick % 2 == 0) { + continue; + } + } + + int block_x = (j * block_width); + int block_y = (y_offset + ((s - board_visible) * block_height)); + SDL_Color mino_color = mino_colors[(board->game.board[j][s] >> 5)]; + + draw_block(ui, block_x, block_y, block_width, block_height, + mino_color, board->scheme.off); + } + } + } + + if (!board->game.game_over) { + for (int s = 0; s < 4; s++) { + for (int j = 0; j < 4; j++) { + if ((board->game.current.m[s]>>(3 - j))&1) { + int block_x = ((j + board->game.current.pos.x) * block_width); + int block_y = y_offset + ((s + board->game.current.pos.y) - board_visible) * block_height; + + SDL_Color mino_color = mino_colors[board->game.current.t]; + draw_block(ui, block_x, block_y, block_width, block_height, + mino_color, board->scheme.off); + + int ghost_y = y_offset + ((s + board->game.current.ghost_y) - board_visible) * block_height; + if (ghost_y != block_y) { + mino_color.a -= 150; + + draw_block(ui, block_x, ghost_y, block_width, block_height, + mino_color, board->scheme.off); + } + } + } + } + } + + if (board->count_down > 0) + board->count_down-=(1.0f/FRAME_RATE); + + SDL_SetRenderTarget(ui->render, NULL); + + SDL_Rect dest = {x, y, w, h}; + //SDL_RenderCopyEx(render, m, NULL, &dest, 0, NULL, SDL_FLIP_NONE); + SDL_RenderCopy(ui->render, m, NULL, &dest); +} + +void draw_held_piece(cetris_ui *ui, SDL_Texture *m, game_board_t* board, int x, int y, int w, int h) { + if (w < 8) return; + if (h < 8) return; + + SDL_SetRenderTarget(ui->render, m); + SDL_RenderClear(ui->render); + + SDL_RenderClear(ui->render); + + int block_width = (w - 4) / 4; + int block_height = (h - 4) / 4; + + // make sure blocks are square + if (block_width > block_height) { + block_width = block_height; + } else { + block_height = block_width; + } + + SDL_Rect hold = {0, 0, w, h}; + + SDL_SetRenderDrawColor(ui->render, board->scheme.off.r, + board->scheme.off.g, board->scheme.off.b, board->scheme.off.a); + + SDL_RenderFillRect(ui->render, &hold); + SDL_RenderDrawRect(ui->render, &hold); + + if (board->game.piece_held) { + for (int s = 0; s < 4; s++) { + for (int j = 0; j < 4; j++) { + if ((board->game.held.m[s]>>(3 - j))&1) { + int block_x = 2 + ((j) * block_width); + int block_y = (s * block_height); + if (board->game.held.t == MINO_I) { + block_y += block_height / 2; + } else if (board->game.held.t != MINO_O) { + block_x += block_width / 2; + } + + SDL_Color mino_color = mino_colors[board->game.held.t]; + draw_block(ui, block_x, block_y, block_width, block_height, + mino_color, board->scheme.off); + + } + } + } + } + + SDL_SetRenderTarget(ui->render, NULL); + + SDL_Rect dest = {x, y, w, h}; + //SDL_RenderCopyEx(render, m, NULL, &dest, 0, NULL, SDL_FLIP_NONE); + SDL_RenderCopy(ui->render, m, NULL, &dest); +} + +void draw_piece_queue(cetris_ui* ui, SDL_Texture *m, game_board_t* board, int x, int y, int w, int h) { + if (w < 8) return; + if (h < 32) return; + + SDL_SetRenderTarget(ui->render, m); + + SDL_RenderClear(ui->render); + + int block_width = ((w - 4) / 4); + int block_height = ((h - 5) / 5) / 3; + + // make sure blocks are square + if (block_width > block_height) { + block_width = block_height; + } else { + block_height = block_width; + } + + SDL_SetRenderDrawColor(ui->render, board->scheme.off.r, + board->scheme.off.g, board->scheme.off.b, board->scheme.off.a); + + SDL_Rect queue = {0, 0, w, h}; + SDL_RenderFillRect(ui->render, &queue); + SDL_RenderDrawRect(ui->render, &queue); + + for (int i = 0; i < 5; i++) { + int index = (board->game.current_index + i); + + uint8_t mino; + if (index <= 6) { + mino = board->game.piece_queue[index]; + } else { + index = index % 7; + mino = board->game.next_queue[index]; + } + + for (int s = 0; s < 4; s++) { + for (int j = 0; j < 4; j++) { + if ((default_matrices[mino][s]>>(3 - j))&1) { + int block_x = 2 + ((j) * block_width); + int block_y = (int)(h * (i/5.0)) + (s * block_height); + if (mino == MINO_I) { + block_y += block_height / 2; + } else if (mino != MINO_O) { + block_x += block_width / 2; + } + + SDL_Color mino_color = mino_colors[mino]; + draw_block(ui, block_x, block_y, block_width, block_height, + mino_color, board->scheme.off); + + } + } + } + } + + SDL_SetRenderTarget(ui->render, NULL); + + SDL_Rect dest = {x, y, w, h}; + //SDL_RenderCopyEx(render, m, NULL, &dest, 0, NULL, SDL_FLIP_NONE); + SDL_RenderCopy(ui->render, m, NULL, &dest); + +} + +void draw_timer(cetris_ui *ui, game_board_t *board, int x, int y) { + char *buf = malloc(50); + long double second = board->game.timer / 1000000.0f; + if (second > 60.0f) { + int minute = (int)(second / 60.0f); + second -= (minute * 60.0f); + format_str(buf, 50, "%02d:%09.6Lf", minute, second); + } else { + format_str(buf, 50, "%.6Lf", second); + } + draw_text(ui, buf, x, y, get_font(ui, 25), board->scheme.text); + + //format_str(buf, 50, "lines remaining: %i", 20 - g.lines); + //draw_text(buf, 20, H - 60, false); + + free(buf); +} + +void draw(cetris_ui* ui) { + SDL_SetRenderDrawColor(ui->render, ui->colors.main.r, + ui->colors.main.g, ui->colors.main.b, ui->colors.main.a); + + SDL_RenderClear(ui->render); + + switch (ui->current_game) { + case SOLO: + draw_image(ui, ui->solo.background, 0, 0, W, H); + draw_board(ui, ui->solo.main, &ui->solo.game_board, (W / 2) - 125, (H / 2) - 250, 250, 500); + draw_held_piece(ui, ui->solo.hold, &ui->solo.game_board, (W / 2) - 230, (H / 2) - 250, 100, 100); + draw_piece_queue(ui, ui->solo.queue, &ui->solo.game_board, (W / 2) + 130, (H / 2) - 250, 100, 450); + draw_timer(ui, &ui->solo.game_board, 20, 20); + draw_image(ui, ui->solo.game_background, (W / 2) - 125, (H / 2) - 250, 250, 500); + break; + } + + SDL_RenderPresent(ui->render); +} + +void load_config(cetris_ui *ui, game_board_t *board) { + + ui->config.keys = (key_bindings_t){ + .key_down = SDLK_DOWN, + .key_right = SDLK_RIGHT, + .key_left = SDLK_LEFT, + .key_rotate_cw = SDLK_UP, + .key_rotate_ccw = 'x', + .key_drop = SDLK_SPACE, + .key_hold = 'c', + .key_restart = 'r' + }; + + board->conf = tetris_ds_config; + board->conf.levels = &tetris_worlds_levels[0]; + board->conf.win_condition = twenty_line_sprint; + board->conf.wait_on_clear = 0; + + ini_parser p; + if (load_ini_file(&p, "config.ini")) { + int das = atoi(get_ini_value(&p, "das", "das")); + board->conf.das_das = das; + + int arr = atoi(get_ini_value(&p, "das", "arr")); + board->conf.das_arr = arr; + + char* drop_delay = get_ini_value(&p, "game", "drop_delay"); + if (drop_delay) board->conf.drop_period = atoi(drop_delay); + + char* next_piece_delay = get_ini_value(&p, "game", "next_piece_delay"); + if (next_piece_delay) board->conf.next_piece_delay = atoi(next_piece_delay); + + char* lock_delay = get_ini_value(&p, "game", "lock_delay"); + if (lock_delay) board->conf.lock_delay = atoi(lock_delay); + + char* force_lock = get_ini_value(&p, "game", "force_lock"); + if (force_lock) board->conf.force_lock = atoi(force_lock); + + char* wait_on_clear = get_ini_value(&p, "game", "wait_on_clear"); + if (wait_on_clear) board->conf.wait_on_clear = atoi(wait_on_clear); + + char* line_delay_clear = get_ini_value(&p, "game", "line_clear_delay"); + if (line_delay_clear) board->conf.line_delay_clear = atoi(line_delay_clear); + + char *dark = get_ini_value(&p, "ui", "dark_mode"); + if (dark && atoi(dark)) { + ui->colors = dark_mode; + } else { + ui->colors = light_mode; + } + } +} + +void load_solo(cetris_ui *ui) { + load_config(ui, &ui->solo.game_board); + ui->solo.game_board.scheme = ui->colors; + + ui->solo.main = SDL_CreateTexture(ui->render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 251, 506); + ui->solo.queue = SDL_CreateTexture(ui->render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 100, 450); + ui->solo.hold = SDL_CreateTexture(ui->render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 100, 100); + ui->solo.background = load_png(ui, "data/background.png"); + ui->solo.game_background = load_png(ui, "data/loli.png"); + SDL_SetTextureAlphaMod(ui->solo.game_background, 40); + SDL_SetTextureBlendMode(ui->solo.game_background, SDL_BLENDMODE_BLEND); + + ui->solo.game_board.count_down = 3; + + init_game(&ui->solo.game_board.game, &ui->solo.game_board.conf); +} + +void handle_key(SDL_Event e, cetris_ui *ui, game_board_t* board) { + int sym; + switch (e.type) { + case SDL_QUIT: exit(0); + case SDL_KEYDOWN: + sym = e.key.keysym.sym; + if (sym == ui->config.keys.key_left) { + move_piece(&board->game, LEFT); + } else if (sym == ui->config.keys.key_right) { + move_piece(&board->game, RIGHT); + } else if (sym == ui->config.keys.key_down) { + move_piece(&board->game, DOWN); + } else if (sym == ui->config.keys.key_drop) { + move_piece(&board->game, HARD_DROP); + } else if (sym == ui->config.keys.key_hold) { + hold_piece(&board->game); + } else if (sym == ui->config.keys.key_rotate_cw) { + move_piece(&board->game, ROTATE_CW); + } else if (sym == ui->config.keys.key_rotate_ccw) { + move_piece(&board->game, ROTATE_CCW); + } else if (sym == ui->config.keys.key_restart) { + cetris_stop_game(&board->game); + board->count_down = 3; + } + break; + case SDL_KEYUP: + sym = e.key.keysym.sym; + if (sym == ui->config.keys.key_left) { + unhold_move(&board->game, LEFT); + } else if (sym == ui->config.keys.key_right) { + unhold_move(&board->game, RIGHT); + } else if (sym == ui->config.keys.key_down) { + unhold_move(&board->game, DOWN); + } else if (sym == ui->config.keys.key_drop) { + unhold_move(&board->game, HARD_DROP); + } else if (sym == ui->config.keys.key_rotate_cw) { + unhold_move(&board->game, ROTATE_CW); + } else if (sym == ui->config.keys.key_rotate_ccw) { + unhold_move(&board->game, ROTATE_CCW); + } + break; + } +} + +void handle_game_events(cetris_ui *ui, game_board_t *board) { + if (board->count_down < 0 && board->game.waiting) { + cetris_start_game(&board->game); + } + + if (board->game.line_event > 0) { + int index; + if (ui->skin.random_audio) { + index = rand() % 5; + } else { + index = board->game.line_combo - 1; + } + if (index > ui->skin.clear_sound_count) + index = ui->skin.clear_sound_count - 1; + + SDL_QueueAudio(ui->audio_device, ui->skin.clear_sound[index].wav_buffer, + ui->skin.clear_sound[index].wav_length); + + SDL_PauseAudioDevice(ui->audio_device, 0); + + board->game.line_event--; + if (board->game.lock_event) + board->game.lock_event = 0; + } + + if (board->game.tetris_event > 0) { + SDL_QueueAudio(ui->audio_device, ui->skin.tetris_sound.wav_buffer, + ui->skin.tetris_sound.wav_length); + + SDL_PauseAudioDevice(ui->audio_device, 0); + board->game.tetris_event--; + } + + if (board->game.lock_event > 0) { + SDL_QueueAudio(ui->audio_device, ui->skin.lock_sound.wav_buffer, + ui->skin.lock_sound.wav_length); + + SDL_PauseAudioDevice(ui->audio_device, 0); + board->game.lock_event--; + } +} + +int main(void) { + cetris_ui ui; + ui.skin.clear_sound_count = 5; + ui.skin.random_audio = true; + + setup_sdl(&ui); + + ui.current_game = SOLO; + + load_solo(&ui); + + SDL_Event e; + for(;;) { + while(SDL_PollEvent(&e)) { + switch (ui.current_game) { + case SOLO: + handle_key(e, &ui, &ui.solo.game_board); + break; + } + } + + switch (ui.current_game) { + case SOLO: + handle_game_events(&ui, &ui.solo.game_board); + break; + } + + draw(&ui); + + SDL_Delay(1000 / FRAME_RATE); + } + + return 0; +} diff --git a/frontends/sdl/cetris_sdl.h b/frontends/sdl/cetris_sdl.h index c6779ac..b562011 100644 --- a/frontends/sdl/cetris_sdl.h +++ b/frontends/sdl/cetris_sdl.h @@ -38,8 +38,18 @@ typedef struct { } font_t; typedef struct { - cetris_game* game; + bool random_audio; + int clear_sound_count; + + audio_clip_t *clear_sound; + audio_clip_t lock_sound; + audio_clip_t tetris_sound; +} cetris_skin_t; + +typedef struct { + cetris_game game; color_scheme_t scheme; + cetris_config conf; float count_down; } game_board_t; @@ -47,7 +57,46 @@ typedef struct { SDL_Texture *main; SDL_Texture *queue; SDL_Texture *hold; + SDL_Texture *background; SDL_Texture *game_background; -} solo_game; + game_board_t game_board; +} solo_game_t; + +typedef struct { + int key_down; + int key_right; + int key_left; + int key_rotate_cw; + int key_rotate_ccw; + int key_drop; + int key_hold; + int key_restart; +} key_bindings_t; + +typedef struct { + key_bindings_t keys; +} config_t; + +enum { + SOLO +}; + +typedef struct { + int current_game; + solo_game_t solo; + color_scheme_t colors; + + SDL_Renderer* render; + SDL_Window *window; + SDL_Surface *screen; + + int font_count; + font_t fonts[10]; + + SDL_AudioDeviceID audio_device; + + config_t config; + cetris_skin_t skin; +} cetris_ui; diff --git a/frontends/sdl/meson.build b/frontends/sdl/meson.build index b38583d..50277a6 100644 --- a/frontends/sdl/meson.build +++ b/frontends/sdl/meson.build @@ -1,4 +1,4 @@ -src = ['sdl_ui.c'] +src = ['cetris_sdl.c'] deps = [cetris] inc = [cetris_inc] diff --git a/frontends/sdl/sdl_ui.c b/frontends/sdl/sdl_ui.c deleted file mode 100644 index 51c1497..0000000 --- a/frontends/sdl/sdl_ui.c +++ /dev/null @@ -1,517 +0,0 @@ -#define SDL_MAIN_HANDLED -#ifdef _WIN32 -#include <SDL.h> -#include <SDL_ttf.h> -#include <SDL_image.h> -#define format_str sprintf_s -#else -#include <SDL2/SDL.h> -#include <SDL2/SDL_ttf.h> -#include <SDL2/SDL_image.h> -#define format_str snprintf -#endif -#include <math.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - -#include <time.h> - -#include <cetris.h> -#include <timer.h> -#include <rules.h> -#include <ini.h> - -#include "cetris_sdl.h" - -#define W 900 -#define H 720 -#define FRAME_RATE 144 - -SDL_Renderer* render; -SDL_Window *window; -SDL_Surface *screen; - -cetris_game g; - -game_board_t main_board; - -int font_count; -font_t fonts[10]; - -bool random_audio; -audio_clip_t clear_sound[5]; -audio_clip_t lock_sound; -audio_clip_t tetris_sound; -SDL_AudioDeviceID audio_device; - -void setup_sdl() { - SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO); - window = SDL_CreateWindow("cetris", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, W, H, SDL_WINDOW_SHOWN); - screen = SDL_GetWindowSurface(window); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); - SDL_SetHint(SDL_HINT_RENDER_DRIVER,"opengl"); - render = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_ACCELERATED); - SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND); - SDL_RenderSetLogicalSize(render, W, H); - if (!render) exit(fprintf(stderr, "err: could not create SDL renderer\n")); - - TTF_Init(); - - for (int i = 0; i < 5; i++) { - char name[25]; - format_str(name, 25, "data/bclear_%i.wav", i); - SDL_LoadWAV(name, &(clear_sound[i].wav_spec), &(clear_sound[i].wav_buffer), &(clear_sound[i].wav_length)); - } - - IMG_Init(IMG_INIT_PNG); - - SDL_LoadWAV("data/lock.wav", &(lock_sound.wav_spec), &(lock_sound.wav_buffer), &(lock_sound.wav_length)); - SDL_LoadWAV("data/tetris.wav", &(tetris_sound.wav_spec), &(tetris_sound.wav_buffer), &(tetris_sound.wav_length)); - audio_device = SDL_OpenAudioDevice(NULL, 0, &(clear_sound[0].wav_spec), NULL, 0); - if (audio_device == 0) printf("failed to open audio device\n"); -} - -TTF_Font* get_font(int size) { - for (int i = 0; i < font_count; i++) { - if (fonts[i].size == size) { - return fonts[i].font; - } - } - fonts[font_count].font = TTF_OpenFont("data/OpenSans-Regular.ttf", size); - TTF_SetFontHinting(fonts[font_count].font, TTF_HINTING_MONO); - fonts[font_count].size = size; - return fonts[font_count++].font; -} - -SDL_Texture* load_png(char* file) { - SDL_Surface* loaded_surface = IMG_Load(file); - return SDL_CreateTextureFromSurface(render, loaded_surface); -} - -void draw_text(char* string, int x, int y, TTF_Font* font, SDL_Color color) { - SDL_Surface *surface; - surface = TTF_RenderText_Solid(font, string, color); - - SDL_Rect message; - message.x = x; - message.y = y; - message.w = surface->w; - message.h = surface->h; - SDL_Texture* tex = SDL_CreateTextureFromSurface(render, surface); - - SDL_RenderCopy(render, tex, NULL, &message); - SDL_DestroyTexture(tex); - SDL_FreeSurface(surface); -} - -void draw_image(SDL_Texture *i, int x, int y, int width, int height) { - SDL_Rect dest = {x, y, width, height}; - SDL_RenderCopy(render, i, NULL, &dest); -} - -void draw_block(int x, int y, int width, int height, SDL_Color c, SDL_Color off) { - SDL_Rect b = {x + 1, y + 1, width - 1, height - 1}; - SDL_SetRenderDrawColor(render, c.r, c.g, c.b, c.a); - SDL_RenderFillRect(render, &b); - SDL_RenderDrawRect(render, &b); - SDL_SetRenderDrawColor(render, off.r, off.g, off.b, off.a); - b.y--; b.x--; b.w+=2; b.h+=2; - SDL_RenderDrawRect(render, &b); -} - -void draw_board(SDL_Texture *m, game_board_t* board, int x, int y, int w, int h) { - SDL_SetRenderTarget(render, m); - SDL_RenderClear(render); - - SDL_Rect background = {0, 0, w, h + 5}; - - SDL_SetRenderDrawColor(render, - board->scheme.off.r - (int)(fmod((double)board->count_down, (double)1.0) * 50), - board->scheme.off.g, board->scheme.off.b, board->scheme.off.a); - - SDL_RenderFillRect(render, &background); - SDL_RenderDrawRect(render, &background); - - SDL_SetRenderDrawColor(render, board->scheme.main.r, - board->scheme.main.g, board->scheme.main.b, board->scheme.main.a); - - int y_offset = 5; - - int board_x = board->game->config.board_x; - int board_y = board->game->config.board_y; - int board_visible = board_y - board->game->config.board_visible; - - int block_width = w / board->game->config.board_x; - int block_height = h / board->game->config.board_visible; - - for (int s = 0; s < board_x + 1; s++) { - int rx = (s * block_width); - SDL_RenderDrawLine(render, rx, 1, rx, h + 5); - } - - for (int j = 0; j < board->game->config.board_visible + 1; j++) { - int ry = y_offset + (j * block_height); - SDL_RenderDrawLine(render, 0, ry, w, ry); - } - - for (int s = board->game->highest_line; s < board_y; s++) { - for (int j = 0; j < board_x; j++) { - if (board->game->board[j][s] & SLOT_OCCUPIED) { - if (g.line_remove_tick[s]) { - if (board->game->tick % 2 == 0) { - continue; - } - } - - int block_x = (j * block_width); - int block_y = (y_offset + ((s - board_visible) * block_height)); - SDL_Color mino_color = mino_colors[(board->game->board[j][s] >> 5)]; - - draw_block(block_x, block_y, block_width, block_height, - mino_color, board->scheme.off); - } - } - } - - if (!board->game->game_over) { - for (int s = 0; s < 4; s++) { - for (int j = 0; j < 4; j++) { - if ((board->game->current.m[s]>>(3 - j))&1) { - int block_x = ((j + board->game->current.pos.x) * block_width); - int block_y = y_offset + ((s + board->game->current.pos.y) - board_visible) * block_height; - - SDL_Color mino_color = mino_colors[board->game->current.t]; - draw_block(block_x, block_y, block_width, block_height, - mino_color, board->scheme.off); - - int ghost_y = y_offset + ((s + board->game->current.ghost_y) - board_visible) * block_height; - if (ghost_y != block_y) { - mino_color.a -= 150; - - draw_block(block_x, ghost_y, block_width, block_height, - mino_color, board->scheme.off); - } - } - } - } - } - - if (board->count_down > 0) board->count_down-=(1.0f/FRAME_RATE); - - SDL_SetRenderTarget(render, NULL); - - SDL_Rect dest = {x, y, w, h}; - //SDL_RenderCopyEx(render, m, NULL, &dest, 0, NULL, SDL_FLIP_NONE); - SDL_RenderCopy(render, m, NULL, &dest); -} - -void draw_held_piece(SDL_Texture *m, game_board_t* board, int x, int y, int w, int h) { - if (w < 8) return; - if (h < 8) return; - - SDL_SetRenderTarget(render, m); - SDL_RenderClear(render); - - SDL_RenderClear(render); - - int block_width = (w - 4) / 4; - int block_height = (h - 4) / 4; - - // make sure blocks are square - if (block_width > block_height) { - block_width = block_height; - } else { - block_height = block_width; - } - - SDL_Rect hold = {0, 0, w, h}; - - SDL_SetRenderDrawColor(render, board->scheme.off.r, - board->scheme.off.g, board->scheme.off.b, board->scheme.off.a); - - SDL_RenderFillRect(render, &hold); - SDL_RenderDrawRect(render, &hold); - - if (board->game->piece_held) { - for (int s = 0; s < 4; s++) { - for (int j = 0; j < 4; j++) { - if ((board->game->held.m[s]>>(3 - j))&1) { - int block_x = 2 + ((j) * block_width); - int block_y = (s * block_height); - if (board->game->held.t == MINO_I) { - block_y += block_height / 2; - } else if (board->game->held.t != MINO_O) { - block_x += block_width / 2; - } - - SDL_Color mino_color = mino_colors[board->game->held.t]; - draw_block(block_x, block_y, block_width, block_height, - mino_color, board->scheme.off); - - } - } - } - } - - SDL_SetRenderTarget(render, NULL); - - SDL_Rect dest = {x, y, w, h}; - //SDL_RenderCopyEx(render, m, NULL, &dest, 0, NULL, SDL_FLIP_NONE); - SDL_RenderCopy(render, m, NULL, &dest); -} - -void draw_piece_queue(SDL_Texture *m, game_board_t* board, int x, int y, int w, int h) { - if (w < 8) return; - if (h < 32) return; - - SDL_SetRenderTarget(render, m); - - SDL_RenderClear(render); - - int block_width = ((w - 4) / 4); - int block_height = ((h - 5) / 5) / 3; - - // make sure blocks are square - if (block_width > block_height) { - block_width = block_height; - } else { - block_height = block_width; - } - - SDL_SetRenderDrawColor(render, board->scheme.off.r, - board->scheme.off.g, board->scheme.off.b, board->scheme.off.a); - - SDL_Rect queue = {0, 0, w, h}; - SDL_RenderFillRect(render, &queue); - SDL_RenderDrawRect(render, &queue); - - for (int i = 0; i < 5; i++) { - int index = (board->game->current_index + i); - - uint8_t mino; - if (index <= 6) { - mino = board->game->piece_queue[index]; - } else { - index = index % 7; - mino = board->game->next_queue[index]; - } - - for (int s = 0; s < 4; s++) { - for (int j = 0; j < 4; j++) { - if ((default_matrices[mino][s]>>(3 - j))&1) { - int block_x = 2 + ((j) * block_width); - int block_y = (int)(h * (i/5.0)) + (s * block_height); - if (mino == MINO_I) { - block_y += block_height / 2; - } else if (mino != MINO_O) { - block_x += block_width / 2; - } - - SDL_Color mino_color = mino_colors[mino]; - draw_block(block_x, block_y, block_width, block_height, - mino_color, board->scheme.off); - - } - } - } - } - - SDL_SetRenderTarget(render, NULL); - - SDL_Rect dest = {x, y, w, h}; - //SDL_RenderCopyEx(render, m, NULL, &dest, 0, NULL, SDL_FLIP_NONE); - SDL_RenderCopy(render, m, NULL, &dest); - -} - -void draw_timer(game_board_t *board, int x, int y) { - char *buf = malloc(50); - long double second = g.timer / 1000000.0f; - if (second > 60.0f) { - int minute = (int)(second / 60.0f); - second -= (minute * 60.0f); - format_str(buf, 50, "%02d:%09.6Lf", minute, second); - } else { - format_str(buf, 50, "%.6Lf", second); - } - draw_text(buf, x, y, get_font(25), board->scheme.text); - - //format_str(buf, 50, "lines remaining: %i", 20 - g.lines); - //draw_text(buf, 20, H - 60, false); - - free(buf); -} - -void draw(solo_game *s) { - SDL_SetRenderDrawColor(render, main_board.scheme.main.r, - main_board.scheme.main.g, main_board.scheme.main.b, main_board.scheme.main.a); - - SDL_RenderClear(render); - - draw_image(s->background, 0, 0, W, H); - draw_board(s->main, &main_board, (W / 2) - 125, (H / 2) - 250, 250, 500); - draw_held_piece(s->hold, &main_board, (W / 2) - 230, (H / 2) - 250, 100, 100); - draw_piece_queue(s->queue, &main_board, (W / 2) + 130, (H / 2) - 250, 100, 450); - draw_timer(&main_board, 20, 20); - draw_image(s->game_background, (W / 2) - 125, (H / 2) - 250, 250, 500); - - SDL_RenderPresent(render); -} - -void load_solo(solo_game* s) { - s->main = SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 251, 506); - s->queue = SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 100, 450); - s->hold = SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 100, 100); -} - -int main(void) { - setup_sdl(); - - cetris_config config = tetris_ds_config; - config.win_condition = twenty_line_sprint; - config.levels = &tetris_worlds_levels[0]; - config.wait_on_clear = 0; - - bool dark_theme = false; - - ini_parser p; - if (load_ini_file(&p, "config.ini")) { - int das = atoi(get_ini_value(&p, "das", "das")); - config.das_das = das; - - int arr = atoi(get_ini_value(&p, "das", "arr")); - config.das_arr = arr; - - char* drop_delay = get_ini_value(&p, "game", "drop_delay"); - if (drop_delay) config.drop_period = atoi(drop_delay); - - char* next_piece_delay = get_ini_value(&p, "game", "next_piece_delay"); - if (next_piece_delay) config.next_piece_delay = atoi(next_piece_delay); - - char* lock_delay = get_ini_value(&p, "game", "lock_delay"); - if (lock_delay) config.lock_delay = atoi(lock_delay); - - char* force_lock = get_ini_value(&p, "game", "force_lock"); - if (force_lock) config.force_lock = atoi(force_lock); - - char* wait_on_clear = get_ini_value(&p, "game", "wait_on_clear"); - if (wait_on_clear) config.wait_on_clear = atoi(wait_on_clear); - - char* line_delay_clear = get_ini_value(&p, "game", "line_clear_delay"); - if (line_delay_clear) config.line_delay_clear = atoi(line_delay_clear); - - char *dark = get_ini_value(&p, "ui", "dark_mode"); - if (dark && atoi(dark)) { - dark_theme = true; - } - } - - if (dark_theme) { - main_board.scheme = dark_mode; - } else { - main_board.scheme = light_mode; - } - - solo_game screen; - load_solo(&screen); - screen.background = load_png("data/background.png"); - screen.game_background = load_png("data/loli.png"); - SDL_SetTextureBlendMode(screen.game_background, SDL_BLENDMODE_BLEND); - SDL_SetTextureAlphaMod(screen.game_background, 40); - - main_board.count_down = 3; - init_game(&g, &config); - main_board.game = &g; - - random_audio = true; - - SDL_Event e; - for(;;) { - while(SDL_PollEvent(&e)) { - switch (e.type) { - case SDL_QUIT: - exit(0); - case SDL_KEYDOWN: - switch (e.key.keysym.sym) { - case SDLK_LEFT: - move_piece(&g, LEFT); break; - case SDLK_RIGHT: - move_piece(&g, RIGHT); break; - case SDLK_DOWN: - move_piece(&g, 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; - case 'r': - cetris_stop_game(&g); - main_board.count_down = 3; - break; - } - break; - case SDL_KEYUP: - switch (e.key.keysym.sym) { - case SDLK_LEFT: - unhold_move(&g, LEFT); break; - case SDLK_RIGHT: - unhold_move(&g, RIGHT); break; - case SDLK_DOWN: - unhold_move(&g, DOWN); break; - case SDLK_SPACE: - unhold_move(&g, HARD_DROP); break; - case SDLK_UP: - unhold_move(&g, ROTATE_CW); break; - case 'x': - unhold_move(&g, ROTATE_CW); break; - case 'z': - unhold_move(&g, ROTATE_CCW); break; - } - break; - } - } - - draw(&screen); - - if (main_board.count_down < 0 && g.waiting) { - cetris_start_game(&g); - } - - if (g.line_event > 0) { - int index; - if (!random_audio) { - index = g.line_combo - 1; - if (index > 3) index = 3; - } else { - index = rand() % 5; - } - int success = SDL_QueueAudio(audio_device, clear_sound[index].wav_buffer, clear_sound[index].wav_length); - SDL_PauseAudioDevice(audio_device, 0); - g.line_event--; - if (g.lock_event) { - g.lock_event = 0; - } - } - - if (g.tetris_event > 0) { - int success = SDL_QueueAudio(audio_device, tetris_sound.wav_buffer, tetris_sound.wav_length); - SDL_PauseAudioDevice(audio_device, 0); - g.tetris_event--; - } - - if (g.lock_event > 0) { - int success = SDL_QueueAudio(audio_device, lock_sound.wav_buffer, lock_sound.wav_length); - SDL_PauseAudioDevice(audio_device, 0); - g.lock_event--; - } - - SDL_Delay(1000 / FRAME_RATE); - } - -} diff --git a/lib/cetris.c b/lib/cetris.c index 35d92e3..38fd07b 100644 --- a/lib/cetris.c +++ b/lib/cetris.c @@ -485,23 +485,17 @@ CETRIS_EXPORT void init_game(cetris_game *g, cetris_config* c) { #endif cetris_config config; - if (!c) { - config = g->config; - } else { - config = *c; - } + if (!c) config = g->config; + else config = *c; // check for config errorsa - if (config.next_piece_delay < config.line_delay_clear) { + if (config.next_piece_delay < config.line_delay_clear) config.next_piece_delay = config.line_delay_clear; - } - if (!config.wait_on_clear) { + if (!config.wait_on_clear) config.next_piece_delay = 0; - } memset(g, 0, sizeof(cetris_game)); - memcpy(&g->config, &config, sizeof(cetris_config)); g->board = (uint8_t **)malloc(sizeof(uint8_t *) * config.board_x); @@ -513,8 +507,8 @@ CETRIS_EXPORT void init_game(cetris_game *g, cetris_config* c) { g->line_remove_tick = (ctick *)malloc(sizeof(ctick) * config.board_y); memset(g->line_remove_tick, 0, sizeof(ctick) * config.board_y); - g->level = config.starting_level; g->waiting = true; + g->level = config.starting_level; g->highest_line = config.board_y; diff --git a/lib/cetris.h b/lib/cetris.h index 523b867..8841df2 100644 --- a/lib/cetris.h +++ b/lib/cetris.h @@ -96,7 +96,8 @@ typedef struct { bool wait_on_clear; ctick *levels; - + + // variable win condition bool (*win_condition)(cetris_game *); } cetris_config; @@ -143,7 +144,7 @@ struct cetris_game { uint8_t line_combo; bool game_over; - // events + // events uint8_t line_event; uint8_t lock_event; uint8_t tetris_event; diff --git a/lib/rules.h b/lib/rules.h index 190e4b7..9d78086 100644 --- a/lib/rules.h +++ b/lib/rules.h @@ -1,3 +1,6 @@ +#ifndef RULES_H +#define RULES_H + #include <cetris.h> cetris_config tetris_ds_config; @@ -5,3 +8,5 @@ ctick tetris_worlds_levels[20]; bool twenty_line_sprint(cetris_game *g); bool forty_line_sprint(cetris_game *g); + +#endif /* RULES_H */ diff --git a/lib/timer.h b/lib/timer.h index 2f29c84..f1f8d5d 100644 --- a/lib/timer.h +++ b/lib/timer.h @@ -1,4 +1,9 @@ +#ifndef TIMER_H +#define TIMER_H + #include <cetris.h> CETRIS_EXPORT void cetris_start_game(cetris_game *g); CETRIS_EXPORT void cetris_stop_game(cetris_game *g); + +#endif /* TIMER_H */ |