diff --git a/.gitignore b/.gitignore index 3a02ff8..c8b5dd8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ tools/* user/* !user/*.* user/*.o +snake/snake .idea cmake-build-debug diff --git a/snake/snake b/snake/snake deleted file mode 100755 index 01f7bb8..0000000 Binary files a/snake/snake and /dev/null differ diff --git a/snake/snake.c b/snake/snake.c index 07a9ff3..ff661ce 100644 --- a/snake/snake.c +++ b/snake/snake.c @@ -46,10 +46,14 @@ typedef enum { snake_direction_bottom = 3, } snake_direction_t; -#define WALKING_ANIM_TIME_SLOW 500 -#define HATCHING_ANIM_TIME_SLOW 200 +#define WALKING_ANIM_TIME_VERY_SLOW 600 +#define HATCHING_ANIM_TIME_VERY_SLOW 400 +#define WALKING_ANIM_TIME_SLOW 450 +#define HATCHING_ANIM_TIME_SLOW 150 #define WALKING_ANIM_TIME_FAST 200 -#define HATCHING_ANIM_TIME_FAST 80 +#define HATCHING_ANIM_TIME_FAST 40 + +#define WEIRD_SOUND 400 typedef enum { waiting_reason_step, @@ -68,6 +72,7 @@ typedef enum { struct Snake { bool is_space_pressed; + bool is_shift_pressed; game_screen_t game_screen; bool have_game; /* from 0 to PLAYABLE_MAPS_COUNT - 1 */ @@ -101,14 +106,15 @@ struct Snake { /* This stuff regulates game flow and animation progress */ waiting_reason_t waiting_reason; uint32_t waiting_time; // Measures how much time we already accumulated - bool is_time_sped_up; + int animation_speed_boost; } snake = {1}; void init_snake() { snake.is_space_pressed = false; + snake.is_shift_pressed = false; snake.game_screen = game_screen_pause; snake.have_game = false; - snake.selected_map_index = 0; + snake.selected_map_index = 6; snake.gamemode_apples_count = SNAKE_GAMEMODE_DEFAULT_APPLES; snake.r_eng = (RandomEngine){.state = time_ms()}; /* snake_head and snake_pupa and snake.world @@ -144,8 +150,10 @@ void start_snake_game() { snake.world[tx][SNAKE_START_Y] = tile_snake_l; } snake.world[SNAKE_START_PUPA_X][SNAKE_START_Y] = tile_pupa_0_l; + snake.snake_head = (ivec2){SNAKE_START_HEAD_X, SNAKE_START_Y}; + snake.snake_tail = (ivec2){SNAKE_START_PUPA_X, SNAKE_START_Y}; for (int app = 0; app < snake.gamemode_apples_count; app++) { - + place_random_apple(); } snake.new_snake_direction = snake.cur_snake_direction = snake_direction_left; snake.is_dying = false; @@ -153,7 +161,7 @@ void start_snake_game() { snake.waiting_reason = waiting_reason_step; snake.waiting_time = 0; - snake.is_time_sped_up = false; + snake.animation_speed_boost = 0; snake.puddle_sprite = -1; } @@ -192,7 +200,7 @@ ivec2 world_walk_direction(ivec2 v, snake_direction_t dir) { } ivec2 get_puddle_center_for_contact(ivec2 head, snake_direction_t dir) { - ivec2 tile_pos = (ivec2){TILE_WIDTH * head.x, TILE_HEIGHT * head.y}; + ivec2 tile_pos = (ivec2){TILE_WIDTH * head.x, TILE_WIDTH * head.y}; if (dir == snake_direction_left || dir == snake_direction_right) { tile_pos.y += TILE_WIDTH / 2; } else if (dir == snake_direction_bottom) { @@ -275,7 +283,7 @@ tile_t construct_snake_tile(snake_direction_t from, snake_direction_t to) { check(false); } -tile_t construct_head_snake_tile(snake_direction_t to) { +tile_t construct_straight_snake_tile(snake_direction_t to) { return construct_snake_tile(get_opposite_direction(to), to); } @@ -335,15 +343,12 @@ snake_direction_t get_zero_pupa_destination(tile_t tile) { check(false); } -/* takes a tile that can possibly become a tail. Either straight snake or pupa */ -bool is_tail_pupa(tile_t tile) { - if (18 <= tile && tile <= 21) - return false; +bool is_tile_pupa(tile_t tile) { if (tile == tile_pupa_0_r || tile == tile_pupa_0_l || tile == tile_pupa_0_b || tile == tile_pupa_0_t) { return true; } - check(false); + return false; } void clear_frame(uint8_t color) { @@ -471,12 +476,10 @@ tile_t correct_game_tile(tile_t x) { } void draw_game_world() { - if (!snake.have_game) - return; - for (int tx = 0; tx < WORLD_WIDTH; tx++) { - for (int ty = 0; ty < WORLD_HEIGHT; ty++) { - tile_t tt = correct_game_tile(snake.world[tx][ty]); + for (int ty = 0; ty <= WORLD_HEIGHT; ty++) { + int iy = ty < WORLD_HEIGHT ? ty : 0; + tile_t tt = correct_game_tile(snake.world[tx][iy]); if (tt == tile_empty) continue; check((uint8_t)tt < TILE_SPRITES); @@ -494,6 +497,13 @@ void draw_game_world() { void draw_game_text() { StringBuilder hud = { 0 }; if (snake.game_screen == game_screen_gaming) { + if (snake.waiting_reason == waiting_reason_postmortum) { + StringBuilder_append_cstr(&hud, "YOU DIED "); + } else if (snake.is_dying) { + StringBuilder_append_cstr(&hud, "LOL "); + } + // StringBuilder_append_u32(&hud, snake.animation_speed_boost); + // StringBuilder_append_cstr(&hud, " "); StringBuilder_append_u32(&hud, snake.score); } else if (snake.game_screen == game_screen_pause) { if (snake.have_game) { @@ -517,7 +527,7 @@ void draw_game_text() { } void draw_game_puddles() { - if (snake.puddle_sprite > 0) { + if (snake.puddle_sprite >= 0) { const PuddleSprite* sprite = &sprite_data.puddle[snake.puddle_sprite]; draw_sprite( snake.puddle_center.x - PUDDLE_WIDTH / 2, @@ -528,8 +538,10 @@ void draw_game_puddles() { void draw_frame() { clear_frame(226); - draw_game_puddles(); - draw_game_world(); + if (snake.have_game) { + draw_game_puddles(); + draw_game_world(); + } draw_game_text(); } @@ -562,6 +574,11 @@ int handle_incoming_keycode_after_halt(uint8_t keycode) { } else if (keycode == (KEYCODE_SPACE | 0x80)) { snake.is_space_pressed = false; } + if (keycode == KEYCODE_SHIFT) { + snake.is_shift_pressed = true; + } else if (keycode == (KEYCODE_SHIFT | 0x80)) { + snake.is_shift_pressed = false; + } if (keycode == KEYCODE_Q && snake.game_screen != game_screen_gaming) { return 1; } @@ -611,7 +628,13 @@ void handle_time_difference(uint32_t time_diff) { return; snake.waiting_time += time_diff; uint32_t target_required_time; - if (snake.is_time_sped_up) { + if (snake.animation_speed_boost == 1) { + if (snake.waiting_reason == waiting_reason_step) { + target_required_time = WALKING_ANIM_TIME_FAST; + } else { + target_required_time = HATCHING_ANIM_TIME_FAST; + } + } else if (snake.animation_speed_boost == 0) { if (snake.waiting_reason == waiting_reason_step) { target_required_time = WALKING_ANIM_TIME_SLOW; } else { @@ -619,9 +642,9 @@ void handle_time_difference(uint32_t time_diff) { } } else { if (snake.waiting_reason == waiting_reason_step) { - target_required_time = WALKING_ANIM_TIME_FAST; + target_required_time = WALKING_ANIM_TIME_VERY_SLOW; } else { - target_required_time = HATCHING_ANIM_TIME_FAST; + target_required_time = HATCHING_ANIM_TIME_VERY_SLOW; } } if (snake.waiting_time > target_required_time) { @@ -633,6 +656,7 @@ void handle_time_difference(uint32_t time_diff) { /* Processing collisions */ if (tile_ahead == tile_apple) { snake.ghost_apples[tile_pos_ahead.x][tile_pos_ahead.y] = 1; + snake.score += 5; } else if (18 <= tile_ahead && tile_ahead <= 49) { snake.waiting_reason = waiting_reason_postmortum; snake.puddle_center = get_puddle_center_for_contact( @@ -652,7 +676,7 @@ void handle_time_difference(uint32_t time_diff) { ivec2 old_head_pos = snake.snake_head; if (tile_ahead == tile_empty || tile_ahead == tile_apple) { set_tile(tile_pos_ahead, - construct_head_snake_tile(snake.new_snake_direction)); + construct_straight_snake_tile(snake.new_snake_direction)); snake.snake_head = tile_pos_ahead; } /* Step 2 */ @@ -661,7 +685,7 @@ void handle_time_difference(uint32_t time_diff) { snake.cur_snake_direction = snake.new_snake_direction; /* Steps 3... */ - if (is_tail_pupa(get_tile(snake.snake_tail))) { + if (is_tile_pupa(get_tile(snake.snake_tail))) { /* Step 3a */ snake_direction_t pupa_dir = get_zero_pupa_destination(get_tile(snake.snake_tail)); ivec2 penultimate_pos = world_walk_direction(snake.snake_tail, pupa_dir); @@ -675,20 +699,27 @@ void handle_time_difference(uint32_t time_diff) { // check, this is important. This is the only place where we move pupa to a new pos // we check for ghost apples. Clear ghost apples and enter // p1-waiting state (instead of remaining in step awaiting state) + if (snake.snake_tail.x == snake.snake_head.x && + snake.snake_tail.y == snake.snake_head.y + ) { + snake.waiting_reason = waiting_reason_postmortum; + return; + } if (snake.ghost_apples[penultimate_pos.x][penultimate_pos.y]) { snake.ghost_apples[penultimate_pos.x][penultimate_pos.y] = 0; - syscall(SYS_set_beep, 5000); + syscall(SYS_set_beep, WEIRD_SOUND); snake.waiting_reason = waiting_reason_p1; /* Nothing changed on screen because we need to wait even * for the first stage of hatching. * We could go to p1 immediately, though */ } + } else { snake_direction_t tail_dir = get_snake_destination(get_tile(snake.snake_tail)); set_tile(snake.snake_tail, construct_zero_pupa(tail_dir)); - syscall(SYS_set_beep, 3000); + syscall(SYS_set_beep, WEIRD_SOUND); snake.waiting_reason = waiting_reason_m1; } @@ -707,11 +738,11 @@ void handle_time_difference(uint32_t time_diff) { } else if (snake.waiting_reason == waiting_reason_cross) { /* Finished pupa hatching */ snake_direction_t tail_dir = get_zero_pupa_destination(get_tile(snake.snake_tail)); - set_tile(snake.snake_tail, construct_zero_pupa(tail_dir)); + set_tile(snake.snake_tail, construct_straight_snake_tile(tail_dir)); syscall(SYS_set_beep, 0); snake.waiting_reason = waiting_reason_step; } else if (snake.waiting_reason == waiting_reason_m1) { - snake.waiting_reason = waiting_reason_cross; + snake.waiting_reason = waiting_reason_zero_pupa; } else if (snake.waiting_reason == waiting_reason_zero_pupa) { /* Finished pupa regrowing */ syscall(SYS_set_beep, 0); @@ -722,7 +753,7 @@ void handle_time_difference(uint32_t time_diff) { void after_awake_action(uint32_t time_diff) { handle_time_difference(time_diff); - snake.is_time_sped_up = false; + snake.animation_speed_boost = 0; draw_frame(); syscall(SYS_swap_frame, (uintptr_t)frame); } @@ -741,7 +772,9 @@ int main(void) { } /* As an additional benefit, we can check if is_space_pressed here */ if (snake.is_space_pressed) { - snake.is_time_sped_up = true; + snake.animation_speed_boost = 1; + } else if (snake.is_shift_pressed) { + snake.animation_speed_boost = -1; } /* snake.is_time_sped_up will be relevant when checking animation end */