I have a working snake
This commit is contained in:
parent
1c2208041b
commit
d76095ca85
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@ tools/*
|
||||
user/*
|
||||
!user/*.*
|
||||
user/*.o
|
||||
snake/snake
|
||||
|
||||
.idea
|
||||
cmake-build-debug
|
||||
|
||||
BIN
snake/snake
BIN
snake/snake
Binary file not shown.
@ -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);
|
||||
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 */
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user