diff options
Diffstat (limited to 'config/tetris.c')
| -rw-r--r-- | config/tetris.c | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/config/tetris.c b/config/tetris.c new file mode 100644 index 0000000..542963a --- /dev/null +++ b/config/tetris.c @@ -0,0 +1,682 @@ +#include "ecex.h" + +#define TETRIS_W 10 +#define TETRIS_H 20 +#define TETRIS_CELLS (TETRIS_W * TETRIS_H) +#define TETRIS_BUF "*tetris*" +#define TETRIS_MODE "tetris-mode" +#define TETRIS_VAR_BOARD "board" +#define TETRIS_VAR_NEXT "next_piece" + +typedef struct tetris_state { + ecex_t *ed; + int *board; + int piece; + int rot; + int x; + int y; + int next_piece; + unsigned int rng; + int score; + int lines; + int level; + int game_over; + int paused; + int last_drop_ms; + int drop_interval_ms; + int log_tick_count; + int log_draw_count; + int log_state_id; +} tetris_state_t; + +static int tetris_idx(int x, int y) { + return y * TETRIS_W + x; +} + +static int tetris_shape_cell(int piece, int rot, int col, int row) { + return ecex_tetris_shape_cell(piece, rot, col, row); +} + +static int *tetris_board(ecex_t *ed, tetris_state_t *s) { + if (!ed || !s) return 0; + return (int *)ecex_var_get_or_alloc(ed, s, TETRIS_VAR_BOARD, (size_t)TETRIS_CELLS, sizeof(int)); +} + +static int tetris_next_piece(ecex_t *ed, tetris_state_t *s) { + return ecex_var_i32(ed, s, TETRIS_VAR_NEXT, 0); +} + +static void tetris_set_next_piece(ecex_t *ed, tetris_state_t *s, int piece) { + if (piece < 0) piece = 0; + if (piece > 6) piece = piece % 7; + ecex_var_i32_set_scalar(ed, s, TETRIS_VAR_NEXT, piece); +} + +static void tetris_clear_board(ecex_t *ed, tetris_state_t *s) { + int *board; + if (!s) { ecex_log("tetris_clear_board: null state"); return; } + board = tetris_board(ed, s); + ecex_log_ptr("tetris_clear_board: board=", board); + ecex_mem_zero(board, (size_t)TETRIS_CELLS * sizeof(int)); + ecex_log("tetris_clear_board: done"); +} + +static int tetris_pick(tetris_state_t *s) { + int piece; + if (!s) { ecex_log("tetris_pick: null state"); return 0; } + piece = ecex_random_bounded(7); + if (piece < 0) piece = 0; + if (piece > 6) piece = piece % 7; + ecex_log_int("tetris_pick: piece=", piece); + return piece; +} + +static int tetris_collides(ecex_t *ed, tetris_state_t *s, int piece, int rot, int px, int py) { + int r; + int c; + + int *board = tetris_board(ed, s); + if (!s || !board) return 1; + for (r = 0; r < 4; ++r) { + for (c = 0; c < 4; ++c) { + int x; + int y; + if (!tetris_shape_cell(piece, rot, c, r)) continue; + + x = px + c; + y = py + r; + if (x < 0 || x >= TETRIS_W || y >= TETRIS_H) return 1; + if (y >= 0 && ecex_i32_get(board, (size_t)tetris_idx(x, y))) return 1; + } + } + + return 0; +} + +static void tetris_spawn(ecex_t *ed, tetris_state_t *s) { + int queued; + + ecex_log_ptr("tetris_spawn: state=", s); + if (!s) return; + + /* The sidebar must show the piece that will become active on the next + * spawn. Keep that queued value in the host variable registry, then consume + * it here and immediately replace it with a freshly-picked preview. */ + queued = tetris_next_piece(ed, s); + if (queued < 0 || queued > 6) queued = tetris_pick(s); + s->piece = queued; + tetris_set_next_piece(ed, s, tetris_pick(s)); + + ecex_log_int("tetris_spawn: current_piece=", s->piece); + ecex_log_int("tetris_spawn: preview_piece=", tetris_next_piece(ed, s)); + + s->rot = 0; + s->x = 3; + s->y = -1; + s->last_drop_ms = 0; + + if (tetris_collides(ed, s, s->piece, s->rot, s->x, s->y)) { + ecex_log("tetris_spawn: immediate collision -> game over"); + s->game_over = 1; + } +} + +static void tetris_reset(ecex_t *ed, tetris_state_t *s) { + ecex_log_ptr("tetris_reset: state=", s); + if (!s) return; + + tetris_clear_board(ed, s); + ecex_log("tetris_reset: after clear"); + /* Avoid calling libc/time-returning host functions from CCDJIT plugin code. + * Seed from stable host-owned addresses instead; good enough for a toy game + * and much safer for the tiny JIT ABI. */ + s->rng = 0x9e3779b9u ^ (unsigned int)(size_t)s ^ (unsigned int)(size_t)tetris_board(ed, s); + ecex_log_int("tetris_reset: rng=", (int)s->rng); + s->score = 0; + s->lines = 0; + s->level = 1; + s->game_over = 0; + s->paused = 0; + s->last_drop_ms = 0; + s->drop_interval_ms = 650; + tetris_set_next_piece(ed, s, tetris_pick(s)); + s->log_tick_count = 0; + s->log_draw_count = 0; + ecex_log("tetris_reset: spawning first piece"); + tetris_spawn(ed, s); + ecex_log("tetris_reset: done"); +} + +static void tetris_lock(ecex_t *ed, tetris_state_t *s) { + ecex_log_ptr("tetris_lock: state=", s); + int r; + int c; + int y; + int cleared = 0; + int *board; + + if (!s) return; + board = tetris_board(ed, s); + if (!board) return; + + for (r = 0; r < 4; ++r) { + for (c = 0; c < 4; ++c) { + int x; + int yy; + if (!tetris_shape_cell(s->piece, s->rot, c, r)) continue; + + x = s->x + c; + yy = s->y + r; + if (x >= 0 && x < TETRIS_W && yy >= 0 && yy < TETRIS_H) { + ecex_i32_set(board, (size_t)tetris_idx(x, yy), s->piece + 1); + } + } + } + + for (y = TETRIS_H - 1; y >= 0; --y) { + int x; + int full = 1; + for (x = 0; x < TETRIS_W; ++x) { + if (!ecex_i32_get(board, (size_t)tetris_idx(x, y))) { + full = 0; + break; + } + } + + if (!full) continue; + ++cleared; + + { + int yy; + for (yy = y; yy > 0; --yy) { + for (x = 0; x < TETRIS_W; ++x) { + ecex_i32_set(board, (size_t)tetris_idx(x, yy), ecex_i32_get(board, (size_t)tetris_idx(x, yy - 1))); + } + } + } + + for (x = 0; x < TETRIS_W; ++x) ecex_i32_set(board, (size_t)tetris_idx(x, 0), 0); + ++y; + } + + if (cleared) { + int points = 0; + if (cleared == 1) points = 100; + else if (cleared == 2) points = 300; + else if (cleared == 3) points = 500; + else points = 800; + + s->score += points * s->level; + s->lines += cleared; + s->level = 1 + s->lines / 10; + s->drop_interval_ms = 650 - (s->level - 1) * 45; + if (s->drop_interval_ms < 80) s->drop_interval_ms = 80; + } + + ecex_log_int("tetris_lock: cleared=", cleared); + tetris_spawn(ed, s); +} + + +static int tetris_piece_has_visible_cells(tetris_state_t *s) { + int r; + int c; + + if (!s) return 0; + for (r = 0; r < 4; ++r) { + for (c = 0; c < 4; ++c) { + int y; + if (!tetris_shape_cell(s->piece, s->rot, c, r)) continue; + y = s->y + r; + if (y >= 0) return 1; + } + } + return 0; +} + +static int tetris_top_out_if_hidden(tetris_state_t *s, const char *where) { + if (!s) return 1; + if (tetris_piece_has_visible_cells(s)) return 0; + ecex_log(where); + ecex_log_int("tetris_top_out: piece=", s->piece); + ecex_log_int("tetris_top_out: y=", s->y); + s->game_over = 1; + return 1; +} + +static int tetris_soft_drop(ecex_t *ed, tetris_state_t *s) { + if (!s) { ecex_log("tetris_soft_drop: null state"); return 0; } + if (s->game_over || s->paused) return 0; + + if (!tetris_collides(ed, s, s->piece, s->rot, s->x, s->y + 1)) { + ++s->y; + return 1; + } + + ecex_log_int("tetris_soft_drop: locking piece=", s->piece); + ecex_log_int("tetris_soft_drop: y=", s->y); + if (tetris_top_out_if_hidden(s, "tetris_soft_drop: hidden lock -> game over")) return 1; + tetris_lock(ed, s); + return 0; +} + + +static int tetris_move_horizontal(ecex_t *ed, tetris_state_t *s, int dx) { + int old_x; + int old_y; + int old_rot; + + if (!s) { ecex_log("tetris_move_horizontal: null state"); return 0; } + if (s->game_over || s->paused) return 0; + + old_x = s->x; + old_y = s->y; + old_rot = s->rot; + + if (!tetris_collides(ed, s, s->piece, s->rot, old_x + dx, old_y)) { + s->x = old_x + dx; + /* Horizontal movement must never vertical-kick the active piece. + * Keep these assignments explicit because plugin/JIT callback bugs are + * otherwise very hard to distinguish from game movement. */ + s->y = old_y; + s->rot = old_rot; + + /* Restart the gravity phase after manual lateral input. This avoids the + * visual "bop" where a key repeat lands on the same frame as gravity + * and appears to couple side movement with a vertical step. */ + s->last_drop_ms = 0; + ecex_log_int("tetris_move_horizontal: x=", s->x); + ecex_log_int("tetris_move_horizontal: y=", s->y); + return 1; + } + + s->x = old_x; + s->y = old_y; + s->rot = old_rot; + return 0; +} + +static void tetris_hard_drop(ecex_t *ed, tetris_state_t *s) { + int moved = 0; + if (!s || s->game_over || s->paused) return; + + while (!tetris_collides(ed, s, s->piece, s->rot, s->x, s->y + 1)) { + ++s->y; + ++moved; + } + + s->score += moved * 2; + if (tetris_top_out_if_hidden(s, "tetris_hard_drop: hidden lock -> game over")) return; + tetris_lock(ed, s); +} + +static tetris_state_t *tetris_state_for_ed(ecex_t *ed) { + buffer_t *buffer; + tetris_state_t *s; + ecex_log("tetris_state_for_ed: enter"); + buffer = ecex_find_buffer(ed, TETRIS_BUF); + ecex_log_ptr("tetris_state_for_ed: buffer=", buffer); + if (!buffer) return 0; + s = (tetris_state_t *)ecex_buffer_renderer_userdata(buffer); + ecex_log_ptr("tetris_state_for_ed: state=", s); + return s; +} + +static int tetris_alpha8(int alpha) { + if (alpha < 0) return 0; + if (alpha > 255) return 255; + return alpha; +} + +static void tetris_color(ecex_draw_context_t *ctx, int cell, int alpha) { + alpha = tetris_alpha8(alpha); + if (cell == 1) ecex_draw_color_rgba8(ctx, 51, 191, 242, alpha); + else if (cell == 2) ecex_draw_color_rgba8(ctx, 242, 217, 51, alpha); + else if (cell == 3) ecex_draw_color_rgba8(ctx, 179, 89, 242, alpha); + else if (cell == 4) ecex_draw_color_rgba8(ctx, 77, 217, 89, alpha); + else if (cell == 5) ecex_draw_color_rgba8(ctx, 242, 64, 64, alpha); + else if (cell == 6) ecex_draw_color_rgba8(ctx, 64, 102, 242, alpha); + else if (cell == 7) ecex_draw_color_rgba8(ctx, 242, 140, 51, alpha); + else ecex_draw_color_rgba8(ctx, 41, 41, 46, alpha); +} + +static void tetris_draw_piece(ecex_draw_context_t *ctx, + int piece, + int rot, + int px, + int py, + int ox, + int oy, + int cell, + int alpha) { + int r; + int c; + + for (r = 0; r < 4; ++r) { + for (c = 0; c < 4; ++c) { + int bx; + int by; + if (!tetris_shape_cell(piece, rot, c, r)) continue; + + bx = px + c; + by = py + r; + if (by < 0) continue; + tetris_color(ctx, piece + 1, alpha); + ecex_draw_rect_i(ctx, ox + bx * cell + 1, oy + by * cell + 1, cell - 2, cell - 2); + } + } +} + +static int tetris_tick(ecex_t *ed, buffer_t *buffer, int now_ms, void *userdata) { + tetris_state_t *s = (tetris_state_t *)userdata; + (void)ed; + (void)buffer; + + if (!s) { ecex_log("tetris_tick: null state"); return 0; } + s->log_tick_count += 1; + if (s->log_tick_count <= 8 || (s->log_tick_count % 60) == 0) { + ecex_log_int("tetris_tick: count=", s->log_tick_count); + ecex_log_int("tetris_tick: now_ms=", now_ms); + } + + if (s->last_drop_ms <= 0) { + s->last_drop_ms = now_ms; + return 1; + } + + if (!s->game_over && !s->paused && now_ms - s->last_drop_ms >= s->drop_interval_ms) { + ecex_log("tetris_tick: dropping piece"); + tetris_soft_drop(ed, s); + s->last_drop_ms = now_ms; + return 1; + } + + return 0; +} + +static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, void *userdata) { + tetris_state_t *s = (tetris_state_t *)userdata; + int max_board_w; + int max_board_h; + int cell; + int board_w; + int board_h; + int ox; + int oy; + int sx; + int sy; + int x; + int y; + int *board; + (void)ed; + (void)buffer; + + if (!s) { ecex_log("tetris_draw: null state"); return 0; } + if (!ctx) { ecex_log("tetris_draw: null ctx"); return 0; } + board = tetris_board(ed, s); + if (!board) { ecex_log("tetris_draw: no registry board"); return 0; } + s->log_draw_count += 1; + if (s->log_draw_count <= 8 || (s->log_draw_count % 60) == 0) { + ecex_log_int("tetris_draw: count=", s->log_draw_count); + ecex_log_int("tetris_draw: ctx_w=", (int)ctx->w); + ecex_log_int("tetris_draw: ctx_h=", (int)ctx->h); + } + + if (s->log_draw_count <= 3) ecex_log("tetris_draw: background"); + ecex_draw_color_rgba8(ctx, 20, 23, 28, 255); + ecex_draw_rect_i(ctx, 0, 0, (int)ctx->w, (int)ctx->h); + + max_board_w = ((int)ctx->content_w * 62) / 100; + max_board_h = (int)ctx->content_h - 24; + cell = max_board_w / TETRIS_W; + if (cell * TETRIS_H > max_board_h) cell = max_board_h / TETRIS_H; + if (cell < 6) cell = 6; + + board_w = cell * TETRIS_W; + board_h = cell * TETRIS_H; + ox = (int)ctx->content_x + 12; + oy = (int)ctx->content_y + 12; + + if (s->log_draw_count <= 3) ecex_log("tetris_draw: board frame"); + ecex_draw_color_rgba8(ctx, 8, 9, 12, 255); + ecex_draw_rect_i(ctx, ox - 6, oy - 6, board_w + 12, board_h + 12); + ecex_draw_color_rgba8(ctx, 97, 102, 115, 255); + ecex_draw_rect_outline_i(ctx, ox - 6, oy - 6, board_w + 12, board_h + 12, 2); + + if (s->log_draw_count <= 3) ecex_log("tetris_draw: cells"); + for (y = 0; y < TETRIS_H; ++y) { + for (x = 0; x < TETRIS_W; ++x) { + int cell_value = ecex_i32_get(board, (size_t)tetris_idx(x, y)); + tetris_color(ctx, cell_value, cell_value ? 255 : 140); + ecex_draw_rect_i(ctx, ox + x * cell + 1, oy + y * cell + 1, cell - 2, cell - 2); + } + } + + if (s->log_draw_count <= 3) ecex_log("tetris_draw: active piece"); + tetris_draw_piece(ctx, s->piece, s->rot, s->x, s->y, ox, oy, cell, 255); + + sx = ox + board_w + 28; + sy = oy; + + if (s->log_draw_count <= 3) ecex_log("tetris_draw: sidebar"); + ecex_draw_color_rgba8(ctx, 235, 235, 214, 255); + ecex_log("tetris_draw: sidebar title"); + ecex_draw_label_i(ctx, sx, sy, 1); + sy += ((int)ctx->line_height * 16) / 10; + + ecex_draw_stat_i(ctx, sx, sy, 2, s->score); + sy += (int)ctx->line_height; + + ecex_draw_stat_i(ctx, sx, sy, 3, s->lines); + sy += (int)ctx->line_height; + + ecex_draw_stat_i(ctx, sx, sy, 4, s->level); + sy += ((int)ctx->line_height * 15) / 10; + + ecex_draw_label_i(ctx, sx, sy, 5); + sy += ((int)ctx->line_height * 8) / 10; + ecex_log_int("tetris_draw: preview_piece=", tetris_next_piece(ed, s)); + ecex_draw_tetris_preview_i(ctx, tetris_next_piece(ed, s), sx, sy, (cell * 72) / 100, 255); + sy += cell * 4; + + ecex_draw_color_rgba8(ctx, 184, 189, 199, 255); + ecex_log("tetris_draw: sidebar help"); + ecex_draw_label_i(ctx, sx, sy, 6); sy += (int)ctx->line_height; + ecex_draw_label_i(ctx, sx, sy, 7); sy += (int)ctx->line_height; + ecex_draw_label_i(ctx, sx, sy, 8); sy += (int)ctx->line_height; + ecex_draw_label_i(ctx, sx, sy, 9); sy += (int)ctx->line_height; + ecex_draw_label_i(ctx, sx, sy, 10); sy += (int)ctx->line_height; + ecex_draw_label_i(ctx, sx, sy, 11); + + if (s->paused || s->game_over) { + ecex_draw_color_rgba8(ctx, 0, 0, 0, 174); + ecex_draw_rect_i(ctx, ox, oy + (board_h * 43) / 100, board_w, ((int)ctx->line_height * 22) / 10); + ecex_draw_color_rgba8(ctx, 255, 235, 89, 255); + ecex_draw_label_i(ctx, ox + cell, oy + (board_h * 43) / 100 + ((int)ctx->line_height * 55) / 100, s->game_over ? 12 : 13); + } + + if (s->log_draw_count <= 3) ecex_log("tetris_draw: done"); + return 0; +} + +static void tetris_free_state(void *userdata) { + tetris_state_t *s = (tetris_state_t *)userdata; + if (!s) return; + ecex_var_free_owner(s->ed, s); + ecex_config_free(s); +} + +static int cmd_tetris(ecex_t *ed) { + buffer_t *buffer; + tetris_state_t *s; + + ecex_log("cmd_tetris: enter"); + ecex_log_ptr("cmd_tetris: ed=", ed); + if (!ed) { ecex_log("cmd_tetris: no editor"); return -1; } + + buffer = ecex_find_buffer(ed, TETRIS_BUF); + ecex_log_ptr("cmd_tetris: existing buffer=", buffer); + if (!buffer) { + ecex_log("cmd_tetris: creating interactive buffer"); + buffer = ecex_create_interactive_buffer(ed, TETRIS_BUF); + } + ecex_log_ptr("cmd_tetris: buffer=", buffer); + if (!buffer) { ecex_log("cmd_tetris: buffer creation failed"); return -1; } + + s = (tetris_state_t *)ecex_buffer_renderer_userdata(buffer); + ecex_log_ptr("cmd_tetris: existing state=", s); + if (!s) { + ecex_log_int("cmd_tetris: allocating state bytes=", (int)sizeof(*s)); + s = (tetris_state_t *)ecex_config_calloc(1, sizeof(*s)); + ecex_log_ptr("cmd_tetris: allocated state=", s); + if (!s) { ecex_log("cmd_tetris: allocation failed"); return -1; } + s->ed = ed; + ecex_log_int("cmd_tetris: registry board bytes=", (int)((size_t)TETRIS_CELLS * sizeof(int))); + ecex_log_ptr("cmd_tetris: registry board=", tetris_board(ed, s)); + if (!tetris_board(ed, s)) { + ecex_log("cmd_tetris: registry board allocation failed"); + ecex_var_free_owner(ed, s); + ecex_config_free(s); + return -1; + } + + tetris_reset(ed, s); + + ecex_log("cmd_tetris: setting renderer"); + if (ecex_buffer_set_renderer(buffer, tetris_draw, s, tetris_free_state, ECEX_RENDER_REPLACE_CONTENT) != 0) { + ecex_log("cmd_tetris: set_renderer failed"); + ecex_config_free(s); + return -1; + } + + ecex_log("cmd_tetris: setting animation"); + if (ecex_buffer_set_animation_ms(buffer, tetris_tick, s, 0, 60) != 0) { + ecex_log("cmd_tetris: set_animation failed"); + ecex_buffer_clear_renderer(buffer); + return -1; + } + } + + ecex_log("cmd_tetris: replacing text"); + if (ecex_buffer_replace_text(buffer, "Tetris renderer. Press n for a new game, q to quit the window.\n") != 0) { + ecex_log("cmd_tetris: replace text failed"); + return -1; + } + + ecex_log("cmd_tetris: setting modified false"); + ecex_buffer_set_modified(buffer, 0); + ecex_log("cmd_tetris: setting major mode"); + if (ecex_buffer_set_major_mode_by_name(ed, buffer, TETRIS_MODE) != 0) { ecex_log("cmd_tetris: set mode failed"); return -1; } + ecex_log("cmd_tetris: switching buffer"); + if (ecex_switch_buffer(ed, TETRIS_BUF) != 0) { ecex_log("cmd_tetris: switch failed"); return -1; } + ecex_log("cmd_tetris: success"); + return 0; +} + +static int cmd_tetris_new(ecex_t *ed) { + ecex_log("cmd_tetris_new: enter"); + tetris_state_t *s = tetris_state_for_ed(ed); + if (!s) return cmd_tetris(ed); + tetris_reset(ed, s); + return 0; +} + +static int cmd_tetris_left(ecex_t *ed) { + ecex_log("cmd_tetris_left: enter"); + tetris_move_horizontal(ed, tetris_state_for_ed(ed), -1); + return 0; +} + +static int cmd_tetris_right(ecex_t *ed) { + ecex_log("cmd_tetris_right: enter"); + tetris_move_horizontal(ed, tetris_state_for_ed(ed), 1); + return 0; +} + +static int cmd_tetris_down(ecex_t *ed) { + ecex_log("cmd_tetris_down: enter"); + tetris_state_t *s = tetris_state_for_ed(ed); + if (s) { + if (tetris_soft_drop(ed, s)) s->score += 1; + s->last_drop_ms = 0; + } + return 0; +} + +static int cmd_tetris_rotate(ecex_t *ed) { + ecex_log("cmd_tetris_rotate: enter"); + tetris_state_t *s = tetris_state_for_ed(ed); + int nr; + + if (!s || s->game_over || s->paused) return 0; + + nr = (s->rot + 1) & 3; + if (!tetris_collides(ed, s, s->piece, nr, s->x, s->y)) { + s->rot = nr; + } else if (!tetris_collides(ed, s, s->piece, nr, s->x - 1, s->y)) { + --s->x; + s->rot = nr; + } else if (!tetris_collides(ed, s, s->piece, nr, s->x + 1, s->y)) { + ++s->x; + s->rot = nr; + } + + return 0; +} + +static int cmd_tetris_drop(ecex_t *ed) { + ecex_log("cmd_tetris_drop: enter"); + tetris_hard_drop(ed, tetris_state_for_ed(ed)); + return 0; +} + +static int cmd_tetris_pause(ecex_t *ed) { + ecex_log("cmd_tetris_pause: enter"); + tetris_state_t *s = tetris_state_for_ed(ed); + if (s && !s->game_over) { + s->paused = !s->paused; + s->last_drop_ms = 0; + } + return 0; +} + +static int cmd_tetris_quit(ecex_t *ed) { + ecex_log("cmd_tetris_quit: enter"); + return ecex_execute_command(ed, "quit-window"); +} + +int ecex_tetris_plugin(ecex_t *ed) { + ecex_log("ecex_tetris_plugin: enter"); + ECEX_CONFIG_MODE(TETRIS_MODE); + ECEX_CONFIG_COMMAND("tetris", cmd_tetris); + ECEX_CONFIG_COMMAND("tetris-new", cmd_tetris_new); + ECEX_CONFIG_COMMAND("tetris-left", cmd_tetris_left); + ECEX_CONFIG_COMMAND("tetris-right", cmd_tetris_right); + ECEX_CONFIG_COMMAND("tetris-down", cmd_tetris_down); + ECEX_CONFIG_COMMAND("tetris-rotate", cmd_tetris_rotate); + ECEX_CONFIG_COMMAND("tetris-drop", cmd_tetris_drop); + ECEX_CONFIG_COMMAND("tetris-pause", cmd_tetris_pause); + ECEX_CONFIG_COMMAND("tetris-quit", cmd_tetris_quit); + + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "LEFT", "tetris-left"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "h", "tetris-left"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "RIGHT", "tetris-right"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "l", "tetris-right"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "DOWN", "tetris-down"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "j", "tetris-down"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "UP", "tetris-rotate"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "k", "tetris-rotate"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "SPC", "tetris-drop"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "p", "tetris-pause"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "n", "tetris-new"); + ECEX_CONFIG_MODE_BIND(TETRIS_MODE, "q", "tetris-quit"); + + ecex_log("ecex_tetris_plugin: registered all commands and keybinds"); + return 0; +} + +#ifndef ECEX_NO_STANDALONE_CONFIG +ECEX_CONFIG_BEGIN + ECEX_CONFIG_INCLUDE(ecex_tetris_plugin); +ECEX_CONFIG_END +#endif |
