/* the code rain from The Matrix, now on the CE changes: - now using constants instead of magic numbers (thanks, TheLastMillenial!) - your theme setting is saved in an appvar (thanks, Roccolox Programs!) - changing directions (thanks, LogicalJoe!) - using gfx_GetSpriteChar() to improve efficiency (thanks, Mateo! thanks, commandz!) - palette swapping (thanks, Mateo! and others that I'm surely forgetting!) thanks to commandz for the SPRITE union thing, couldn't have done it otherwise! maybe make an exit menu, red pill for exit, blue pill for stay? (Zeroko's idea?) */ #include #include #include #include #include #include #include enum COLOURS { // if you're wondering where this order came from... RED = 0, GREEN, YELLOW, BLUE, PURPLE, TEAL, MAGENTA, ORANGE, RAINBOW }; uint16_t PALETTE_THEMES[9][9] = { // same order as the enum { 0, 4096, 8192, 12288, 16384, 20480, 24576, 28672, 29592 }, // red { 0, 128, 256, 384, 512, 640, 768, 896, 29592 }, // green { 0, 8320, 12544, 16768, 20992, 25216, 29440, 29568, 29592 }, // yellow { 0, 8, 264, 16, 144, 400, 280, 536, 4760 }, // blue { 0, 4232, 4112, 4368, 12560, 12568, 16792, 21144, 29592 }, // purple { 0, 264, 392, 520, 4744, 9104, 13200, 21392, 29592 }, // teal { 0, 8200, 16520, 16400, 20624, 24728, 29080, 29336, 29592 }, // magenta { 0, 8320, 12416, 16640, 20736, 24832, 28928, 29184, 29592 }, // orange { 0, 28672, 29056, 29568, 896, 648, 4632, 12440, 29592 } // rainbow }; #define COLOURS_PER_THEME 9 const char* file_name = "CORATHSE"; // short for COde RAin THeme SEtting int selected_theme = GREEN; void set_colour_theme(int theme) { if (theme >= 9) { // just in case theme = GREEN; } selected_theme = theme; for (int c = 0; c < COLOURS_PER_THEME; c++) { // swap palette colours gfx_palette[c] = PALETTE_THEMES[theme][c]; } } // each character is 10 pixels tall and 8 pixels wide. i think. // this makes the screen a 40 by 24 grid of tiles #define CHAR_WIDTH 8 #define CHAR_HEIGHT 10 #define SCREEN_WIDTH LCD_WIDTH / CHAR_WIDTH #define SCREEN_HEIGHT LCD_HEIGHT / CHAR_HEIGHT + 1 typedef struct TILE { int character; int value; int decrement; } TILE; TILE tiles[SCREEN_WIDTH][SCREEN_HEIGHT]; void update_tile(TILE* t) { if (t->value < 0) { return; } t->value -= t->decrement; if (t->value < (-1 * t->decrement)) { t->value = 0; // sigh... } } typedef union SPRITE { gfx_sprite_t sprite; uint8_t data[66]; } SPRITE; SPRITE sprites[80][9]; void draw_tile(TILE* t, int x, int y) { if (t->value < 0) { return; } int draw_x = CHAR_WIDTH * x; int draw_y = CHAR_HEIGHT * y; gfx_Sprite(&(sprites[t->character][t->value].sprite), draw_x, draw_y); } char* available_characters = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnoprstuvwxyz#*+=-_@~?<>&(){}[];"; void generate_sprites() { for (int j = 0; j < 9; j++) { gfx_SetTextFGColor(j); // uint16_t colour = (2 << j) - 1; // gfx_palette[j] = gfx_RGBTo1555(colour, colour, colour); for (int i = 0; i < 80; i++) { gfx_sprite_t* temp_sprite = gfx_GetSpriteChar(available_characters[i]); gfx_FlipSpriteY(temp_sprite, &(sprites[i][j].sprite)); } } } #define MAX_TRAILS 128 // i don't think i'll actually have this many trails... but it's better to be safe! enum TRAIL_DIRECTION { DOWN = 0, UP, LEFT, RIGHT, DIRECTIONS_N }; int direction; typedef struct TRAIL { int x; int y; bool used; int decrement; } TRAIL; TRAIL trails[MAX_TRAILS]; void add_trail() { for (int c = 0; c < MAX_TRAILS; c++) { if (trails[c].used) continue; TRAIL* t = &trails[c]; int x = rand() % SCREEN_WIDTH; int y = rand() % SCREEN_HEIGHT; switch (direction) { case DOWN: if (tiles[x][0].value <= 0) { t->x = x; t->y = 0; } break; case UP: if (tiles[x][SCREEN_HEIGHT - 1].value <= 0) { t->x = x; t->y = SCREEN_HEIGHT - 1; } break; case LEFT: if (tiles[SCREEN_WIDTH - 1][y].value <= 0) { t->x = SCREEN_WIDTH; t->y = y; } break; case RIGHT: if (tiles[0][y].value <= 0) { t->x = 0; t->y = y; } break; } t->used = true; t->decrement = rand() % 2 + 1; return; } } void update_trail(TRAIL* t) { switch (direction) { case DOWN: if (t->y >= SCREEN_HEIGHT) { t->used = false; return; } t->y++; break; case UP: if (t->y <= 0) { t->used = false; return; } t->y--; break; case LEFT: if (t->x <= 0) { t->used = false; return; } t->x--; break; case RIGHT: if (t->x >= SCREEN_WIDTH) { t->used = false; return; } t->x++; break; } TILE* tile = &tiles[t->x][t->y]; tile->character = rand() % 80; tile->decrement = t->decrement; tile->value = 8; } #define CODE_DENSITY 4 int main() { bool running = true; gfx_Begin(); gfx_SetTextBGColor(0); gfx_SetTextFGColor(255); gfx_SetTextConfig(gfx_text_noclip); gfx_SetMonospaceFont(CHAR_WIDTH); gfx_FillScreen(0); gfx_PrintStringXY("generating sprites...", 5, 5); generate_sprites(); // set up the tiles for (int x = 0; x < SCREEN_WIDTH; x++) { for (int y = 0; y < SCREEN_HEIGHT; y++) { tiles[x][y].value = -1; } } // detect the colour theme set, if there is any ti_var_t read_settings; ti_CloseAll(); if (!(read_settings = ti_Open(file_name, "r"))) { // theme setting not set, use default green theme set_colour_theme(GREEN); direction = DOWN; } else { int theme; ti_Read(&theme, sizeof(int), 1, read_settings); ti_Read(&direction, sizeof(int), 1, read_settings); if (direction > DIRECTIONS_N) { direction = direction % DIRECTIONS_N; } set_colour_theme(theme); } gfx_FillScreen(0); do { for (int c = 0; c < MAX_TRAILS; c++) { update_trail(&trails[c]); } for (int x = 0; x < SCREEN_WIDTH; x++) { for (int y = 0; y < SCREEN_HEIGHT; y++) { update_tile(&tiles[x][y]); draw_tile(&tiles[x][y], x, y); } }; for (int n = 0; n < CODE_DENSITY; n++) { add_trail(); } kb_Scan(); running = !kb_IsDown(kb_KeyClear); // oh great... a mountain of if statements, Yanderedev style... // a necessary evil here? // if only there was a more elegant way to doing this... if (kb_IsDown(kb_Key1)) { set_colour_theme(RED); } if (kb_IsDown(kb_Key2)) { set_colour_theme(GREEN); } if (kb_IsDown(kb_Key3)) { set_colour_theme(YELLOW); } if (kb_IsDown(kb_Key4)) { set_colour_theme(BLUE); } if (kb_IsDown(kb_Key5)) { set_colour_theme(PURPLE); } if (kb_IsDown(kb_Key6)) { set_colour_theme(TEAL); } if (kb_IsDown(kb_Key7)) { set_colour_theme(MAGENTA); } if (kb_IsDown(kb_Key8)) { set_colour_theme(ORANGE); } if (kb_IsDown(kb_Key9)) { set_colour_theme(RAINBOW); } // change the direction if (kb_IsDown(kb_KeyDown)) { direction = DOWN; } if (kb_IsDown(kb_KeyUp)) { direction = UP; } if (kb_IsDown(kb_KeyLeft)) { direction = LEFT; } if (kb_IsDown(kb_KeyRight)) { direction = RIGHT; } } while (running); // save the stuff ti_var_t write_settings = ti_Open(file_name, "w"); ti_Write(&selected_theme, sizeof(int), 1, write_settings); ti_Write(&direction, sizeof(int), 1, write_settings); ti_Close(write_settings); gfx_End(); return 0; }