From 6245639dafb1171f98244b1a30e2d82b737e80ec Mon Sep 17 00:00:00 2001 From: Stale Date: Sun, 24 Apr 2022 06:54:50 -0300 Subject: [PATCH] Matrix scrolling text animation (#283) --- res/config.ini | 4 +- src/draw.c | 267 +++++++++++++++++++++++++++++++++++++++++++++++-- src/draw.h | 27 ++++- 3 files changed, 285 insertions(+), 13 deletions(-) diff --git a/res/config.ini b/res/config.ini index f53ecab..72b333b 100644 --- a/res/config.ini +++ b/res/config.ini @@ -2,7 +2,9 @@ #animate = false #animate = true -# the active animation (only animation '0' available for now) +# the active animation +# 0 -> PSX DOOM fire (default) +# 1 -> CMatrix #animation = 0 # the char used to mask the password diff --git a/src/draw.c b/src/draw.c index 4ce7ad1..f515705 100644 --- a/src/draw.c +++ b/src/draw.c @@ -69,11 +69,22 @@ void draw_init(struct term_buf* buf) #endif } +static void doom_free(struct term_buf* buf); +static void matrix_free(struct term_buf* buf); + void draw_free(struct term_buf* buf) { if (config.animate) { - free(buf->tmp_buf); + switch (config.animation) + { + case 0: + doom_free(buf); + break; + case 1: + matrix_free(buf); + break; + } } } @@ -481,18 +492,118 @@ static void doom_init(struct term_buf* buf) { buf->init_width = buf->width; buf->init_height = buf->height; + buf->astate.doom = malloc(sizeof(struct doom_state)); - uint16_t tmp_len = buf->width * buf->height; - buf->tmp_buf = malloc(tmp_len); - tmp_len -= buf->width; - - if (buf->tmp_buf == NULL) + if (buf->astate.doom == NULL) { dgn_throw(DGN_ALLOC); } - memset(buf->tmp_buf, 0, tmp_len); - memset(buf->tmp_buf + tmp_len, DOOM_STEPS - 1, buf->width); + uint16_t tmp_len = buf->width * buf->height; + buf->astate.doom->buf = malloc(tmp_len); + tmp_len -= buf->width; + + if (buf->astate.doom->buf == NULL) + { + dgn_throw(DGN_ALLOC); + } + + memset(buf->astate.doom->buf, 0, tmp_len); + memset(buf->astate.doom->buf + tmp_len, DOOM_STEPS - 1, buf->width); +} + +static void doom_free(struct term_buf* buf) +{ + free(buf->astate.doom->buf); + free(buf->astate.doom); +} + +// Adapted from cmatrix +static void matrix_init(struct term_buf* buf) +{ + buf->init_width = buf->width; + buf->init_height = buf->height; + buf->astate.matrix = malloc(sizeof(struct matrix_state)); + struct matrix_state* s = buf->astate.matrix; + + if (s == NULL) + { + dgn_throw(DGN_ALLOC); + } + + uint16_t len = buf->height + 1; + s->grid = malloc(sizeof(struct matrix_dot*) * len); + + if (s->grid == NULL) + { + dgn_throw(DGN_ALLOC); + } + + len = (buf->height + 1) * buf->width; + (s->grid)[0] = malloc(sizeof(struct matrix_dot) * len); + + if ((s->grid)[0] == NULL) + { + dgn_throw(DGN_ALLOC); + } + + for (int i = 1; i <= buf->height; ++i) + { + s->grid[i] = s->grid[i - 1] + buf->width; + + if (s->grid[i] == NULL) + { + dgn_throw(DGN_ALLOC); + } + } + + s->length = malloc(buf->width * sizeof(int)); + + if (s->length == NULL) + { + dgn_throw(DGN_ALLOC); + } + + s->spaces = malloc(buf->width * sizeof(int)); + + if (s->spaces == NULL) + { + dgn_throw(DGN_ALLOC); + } + + s->updates = malloc(buf->width * sizeof(int)); + + if (s->updates == NULL) + { + dgn_throw(DGN_ALLOC); + } + + // Initialize grid + for (int i = 0; i <= buf->height; ++i) + { + for (int j = 0; j <= buf->width - 1; j += 2) + { + s->grid[i][j].val = -1; + } + } + + for (int j = 0; j < buf->width; j += 2) + { + s->spaces[j] = (int) rand() % buf->height + 1; + s->length[j] = (int) rand() % (buf->height - 3) + 3; + s->grid[1][j].val = ' '; + s->updates[j] = (int) rand() % 3 + 1; + } +} + +static void matrix_free(struct term_buf* buf) +{ + free(buf->astate.matrix->grid[0]); + free(buf->astate.matrix->grid); + free(buf->astate.matrix->length); + free(buf->astate.matrix->spaces); + free(buf->astate.matrix->updates); + free(buf->astate.matrix); } void animate_init(struct term_buf* buf) @@ -501,11 +612,16 @@ void animate_init(struct term_buf* buf) { switch(config.animation) { - default: + case 0: { doom_init(buf); break; } + case 1: + { + matrix_init(buf); + break; + } } } } @@ -534,7 +650,7 @@ static void doom(struct term_buf* term_buf) uint16_t dst; uint16_t w = term_buf->init_width; - uint8_t* tmp = term_buf->tmp_buf; + uint8_t* tmp = term_buf->astate.doom->buf; if ((term_buf->width != term_buf->init_width) || (term_buf->height != term_buf->init_height)) { @@ -573,6 +689,130 @@ static void doom(struct term_buf* term_buf) } } +// Adapted from cmatrix +static void matrix(struct term_buf* buf) +{ + static int frame = 3; + const int frame_delay = 8; + static int count = 0; + bool first_col; + struct matrix_state* s = buf->astate.matrix; + + // Allowed codepoints + const int randmin = 33; + const int randnum = 123 - randmin; + // Chars change mid-scroll + const bool changes = true; + + if ((buf->width != buf->init_width) || (buf->height != buf->init_height)) + { + return; + } + + count += 1; + if (count > frame_delay) { + frame += 1; + if (frame > 4) frame = 1; + count = 0; + + for (int j = 0; j < buf->width; j += 2) + { + int tail; + if (frame > s->updates[j]) + { + if (s->grid[0][j].val == -1 && s->grid[1][j].val == ' ') + { + if (s->spaces[j] > 0) + { + s->spaces[j]--; + } else { + s->length[j] = (int) rand() % (buf->height - 3) + 3; + s->grid[0][j].val = (int) rand() % randnum + randmin; + s->spaces[j] = (int) rand() % buf->height + 1; + } + } + + int i = 0, seg_len = 0; + first_col = 1; + while (i <= buf->height) + { + // Skip over spaces + while (i <= buf->height + && (s->grid[i][j].val == ' ' || s->grid[i][j].val == -1)) + { + i++; + } + + if (i > buf->height) break; + + // Find the head of this col + tail = i; + seg_len = 0; + while (i <= buf->height + && (s->grid[i][j].val != ' ' && s->grid[i][j].val != -1)) + { + s->grid[i][j].is_head = false; + if (changes) + { + if (rand() % 8 == 0) + s->grid[i][j].val = (int) rand() % randnum + randmin; + } + i++; + seg_len++; + } + + // Head's down offscreen + if (i > buf->height) + { + s->grid[tail][j].val = ' '; + continue; + } + + s->grid[i][j].val = (int) rand() % randnum + randmin; + s->grid[i][j].is_head = true; + + if (seg_len > s->length[j] || !first_col) { + s->grid[tail][j].val = ' '; + s->grid[0][j].val = -1; + } + first_col = 0; + i++; + } + } + } + } + + uint32_t blank; + utf8_char_to_unicode(&blank, " "); + + for (int j = 0; j < buf->width; j += 2) { + for (int i = 1; i <= buf->height; ++i) + { + uint32_t c; + int fg = TB_GREEN; + int bg = TB_DEFAULT; + + if (s->grid[i][j].val == -1 || s->grid[i][j].val == ' ') + { + tb_change_cell(j, i - 1, blank, fg, bg); + continue; + } + + char tmp[2]; + tmp[0] = s->grid[i][j].val; + tmp[1] = '\0'; + if(utf8_char_to_unicode(&c, tmp)) + { + if (s->grid[i][j].is_head) + { + fg = TB_WHITE | TB_BOLD; + } + tb_change_cell(j, i - 1, c, fg, bg); + } + } + } +} + void animate(struct term_buf* buf) { buf->width = tb_width(); @@ -582,11 +822,16 @@ void animate(struct term_buf* buf) { switch(config.animation) { - default: + case 0: { doom(buf); break; } + case 1: + { + matrix(buf); + break; + } } } } diff --git a/src/draw.h b/src/draw.h index 6b040b0..ce4cf60 100644 --- a/src/draw.h +++ b/src/draw.h @@ -19,6 +19,31 @@ struct box uint32_t right; }; +struct matrix_dot +{ + int val; + bool is_head; +}; + +struct matrix_state +{ + struct matrix_dot** grid; + int* length; + int* spaces; + int* updates; +}; + +struct doom_state +{ + uint8_t* buf; +}; + +union anim_state +{ + struct doom_state* doom; + struct matrix_state* matrix; +}; + struct term_buf { uint16_t width; @@ -34,7 +59,7 @@ struct term_buf uint16_t box_width; uint16_t box_height; - uint8_t* tmp_buf; + union anim_state astate; }; void draw_init(struct term_buf* buf);