diff --git a/Makefile b/Makefile index b3a670f..d3a2afd 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ WARNINGS := -Wall -Wextra -pedantic \ -Wstrict-overflow=5 -fstrict-overflow -Winline CWARNINGS := -Wstrict-prototypes -Wmissing-prototypes \ -Wold-style-definition -Wold-style-declaration -LINKFLAGS := -mmcu=$(MCU) -Os -Wl,--gc-sections +LINKFLAGS := -mmcu=$(MCU) -O3 -Wl,--gc-sections COMMONFLAGS := $(WARNINGS) $(LINKFLAGS) -fdata-sections -ffunction-sections CFLAGS := $(COMMONFLAGS) $(CWARNINGS) -std=gnu99 @@ -104,6 +104,10 @@ $(BUILD)/%_2: $(IMG)/%.png mkdir -p $(dir $@) $(MAGICK) $< -depth 2 -colorspace gray Y:$@ +$(BUILD)/%_1: $(IMG)/%.png + mkdir -p $(dir $@) + $(MAGICK) $< -depth 1 -colorspace gray Y:$@ + erase: dfu-programmer $(MCU) erase diff --git a/img/levels.png b/img/levels.png new file mode 100644 index 0000000..e21b699 Binary files /dev/null and b/img/levels.png differ diff --git a/programs.mk b/programs.mk index db2eda9..6eed606 100644 --- a/programs.mk +++ b/programs.mk @@ -1,2 +1,2 @@ snake_OBJECTS := $(call objs,*) $(call objs,**/*) \ - segment24x32_2 snake_2 sqr8x8_2 + segment24x32_2 snake_2 sqr8x8_2 levels_1 diff --git a/src/snake/board.c b/src/snake/board.c index ecd5139..d6519ca 100644 --- a/src/snake/board.c +++ b/src/snake/board.c @@ -6,6 +6,7 @@ #include "grid.h" #include "hw/buttons.h" #include "hw/display.h" +#include "levels.h" #include "markers.h" #include "rng.h" #include "scoreio.h" @@ -24,14 +25,20 @@ static const point_t DIRECTIONS[4] = { bool demo; static point_t demo_apples[] PROGMEM = { - {15, 6}, - {10, 8}, - {25, 9}, - {2, 20} + {11, 12}, + {28, 18}, + {15, 29}, + {1, 28}, + {25, 3} }; static uint8_t apple_index; -point_t get_demo_apple(void) { +static uint8_t level_countdown; +static uint8_t level; +static int8_t step; +static char level_text[] = "LEVEL XX"; + +static point_t get_demo_apple(void) { point_t apple; for(uint8_t i = 0; i < sizeof(apple); i++) { @@ -76,7 +83,7 @@ static void place_apple(void) { pos.x = -1; } - while(pos.x == -1 || read_cell(pos) == SNAKE_COLOR) { + while(pos.x == -1 || read_cell(pos) != 0) { pos.x = rng8() & GRID_MASK; pos.y = rng8() & GRID_MASK; } @@ -84,13 +91,70 @@ static void place_apple(void) { write_cell(pos, APPLE_COLOR); } +static void start_level(void) { + uint16_t real_score = get_score(score); + level = real_score >> 4; + load_level(level & 3); + + uint8_t tens = level / 10; + level_text[sizeof(level_text) - 3] = '0' + tens; + level_text[sizeof(level_text) - 2] = '0' + (level - tens * 10); + + // Increase speed every time the levels repeat + step = 5 - (level >> 2); + if(step < 0) step = 0; + + clear_turns(); + tail = head = (marker_t){{1, 1}, {0, 1}}; + tail.position.y -= get_score(score) + 1; + + int8_t y = tail.position.y + 1; + if(y < 0) y = 0; + + for(; y <= head.position.y; y++) { + write_cell((point_t){tail.position.x, y}, SNAKE_COLOR); + } + level_countdown = 175; + place_apple(); +} + void tick_board(void) { + static uint8_t step_counter; + + if(level_countdown > 0) { + --level_countdown; + + // Don't draw level number on the demo + if(!demo && (level_countdown & 31) == 0) { + init_font_sqr(); + + if((level_countdown & 63) == 0) { + draw_string(LCD_SIZE.y / 2, 0, level_text, CENTER); + } else { + font_blitter = blit_clear; + draw_string(LCD_SIZE.y / 2, 0, level_text, CENTER); + font_blitter = blit_2_palette; + } + + init_font_seg(); + } + return; + } + if(demo) { tick_anybutton(); } else { process_buttons(); } - if(frame_counter & 3 || tick_func_last == tick_board) return; + + // We've been switched out + if(tick_func_last == tick_board) return; + + if(++step_counter == step) { + step_counter = 0; + } else { + return; + } move_head(); bool eaten_apple = read_cell(head.position) == APPLE_COLOR; @@ -99,13 +163,23 @@ void tick_board(void) { // Handle previous apple eaten int8_t i = increment_score(&score); draw_score_suffix(SCORE_LEFT, SCORE_Y, score, i); + + if((get_score(score) & 15) == 0) { + if(demo) { + // Don't let the demo get past level 0 + context_switch(setup_mainmenu, tick_mainmenu); + } else { + start_level(); + } + return; + } } else { // Clear up tail move_tail(); write_cell(tail.position, 0); } - if(read_cell(head.position) == SNAKE_COLOR) { + if(!eaten_apple && read_cell(head.position) != 0) { // Game over if(demo) { context_switch(setup_mainmenu, tick_mainmenu); @@ -152,6 +226,5 @@ void setup_board(void) { draw_score_suffix(SCORE_RIGHT, SCORE_Y, hiscores[0], 0); if(demo) init_font_sqr(); - clear_grid(); - place_apple(); + start_level(); } diff --git a/src/snake/levels.c b/src/snake/levels.c new file mode 100644 index 0000000..c10772a --- /dev/null +++ b/src/snake/levels.c @@ -0,0 +1,25 @@ +#include "blob.h" +#include "grid.h" +#include "levels.h" + +#define LEVEL_BYTES (32*32 / 8) +#define OBSTACLE_COLOR (0x7475) + +USEBLOB(levels_1); + +void load_level(uint8_t level) { + clear_grid(); + if(level == 0) return; + + const uint8_t * level_data = BLOB(levels_1) + LEVEL_BYTES * (level - 1); + + for(uint8_t y = 0; y < 32; y++) { + for(uint8_t x = 0; x < 32; x += 8) { + uint8_t b = pgm_read_byte(level_data++); + + for(uint8_t i = 0; i < 8; i++) { + write_cell((point_t){x + i, y}, b >> (7 - i) & 1 ? OBSTACLE_COLOR : 0); + } + } + } +} diff --git a/src/snake/levels.h b/src/snake/levels.h new file mode 100644 index 0000000..96f53c4 --- /dev/null +++ b/src/snake/levels.h @@ -0,0 +1,11 @@ +#ifndef LEVELS_H_ +#define LEVELS_H_ + +#include + +/** + * Loads the given level into the grid. + */ +void load_level(uint8_t level); + +#endif diff --git a/src/snake/markers.c b/src/snake/markers.c index 700a765..ab820fb 100644 --- a/src/snake/markers.c +++ b/src/snake/markers.c @@ -10,24 +10,32 @@ // Simple square static marker_t demo_moves[] PROGMEM = { - {{16, 24}, {1, 0}}, - {{26, 24}, {0, 1}}, - {{26, 6}, {-1, 0}}, - {{5, 6}, {0, 1}}, - {{5, 13}, {1, 0}}, - {{11, 13}, {0, -1}}, - {{11, 11}, {-1, 0}}, - {{8, 11}, {0, -1}}, - {{8, 8}, {1, 0}}, - {{21, 8}, {0, 1}}, - {{21, 16}, {1, 0}}, - {{25, 16}, {0, -1}}, - {{25, 8}, {1, 0}}, - {{2, 8}, {0, 1}}, - {{2, 26}, {1, 0}}, - {{12, 26}, {0, -1}}, - {{12, 16}, {1, 0}}, - {{16, 16}, {0, 1}} + {{1, 6}, {1, 0}}, + {{4, 6}, {0, -1}}, + {{4, 29}, {1, 0}}, + {{18, 29}, {0, -1}}, + {{18, 19}, {1, 0}}, + {{24, 19}, {0, -1}}, + {{24, 14}, {-1, 0}}, + {{18, 14}, {0, -1}}, + {{18, 11}, {1, 0}}, + {{23, 11}, {0, 1}}, + {{23, 13}, {1, 0}}, + {{5, 13}, {0, 1}}, + {{5, 16}, {-1, 0}}, + {{28, 16}, {0, 1}}, + {{28, 19}, {1, 0}}, + {{2, 19}, {0, -1}}, + {{2, 18}, {1, 0}}, + {{7, 18}, {0, -1}}, + {{7, 12}, {1, 0}}, + {{13, 12}, {0, -1}}, + {{13, 3}, {1, 0}}, + {{29, 3}, {0, -1}}, + {{29, 30}, {-1, 0}}, + {{25, 30}, {0, -1}}, + {{25, 27}, {1, 0}}, + {{1, 27}, {0, 1}} }; static marker_t demo_move; static uint8_t demo_index; @@ -57,9 +65,22 @@ void setup_markers(void) { move_head(); } -static inline void apply_direction(marker_t * marker) { - marker->position.x = (marker->position.x + marker->direction.x) & GRID_MASK; - marker->position.y = (marker->position.y + marker->direction.y) & GRID_MASK; +static void apply_direction(marker_t * marker) { + if(marker->direction.x > 0) { + ++marker->position.x; + if(marker->position.x == GRID_SIZE) { marker->position.x = 0; } + } else if(marker->direction.x < 0) { + --marker->position.x; + if(marker->position.x == -1) { marker->position.x = GRID_SIZE - 1; } + } + + if(marker->direction.y > 0) { + ++marker->position.y; + if(marker->position.y == GRID_SIZE) { marker->position.y = 0; } + } else if(marker->direction.y < 0) { + --marker->position.y; + if(marker->position.y == -1) { marker->position.y = GRID_SIZE - 1; } + } } void move_head(void) { diff --git a/src/snake/scoreio.h b/src/snake/scoreio.h index ab17608..7864c7c 100644 --- a/src/snake/scoreio.h +++ b/src/snake/scoreio.h @@ -72,4 +72,11 @@ static inline void get_score_text(const score_t score, char * const str) { str[4] = '\0'; } +static inline uint16_t get_score(const score_t score) { + return score.score[0] * 1000 + + score.score[1] * 100 + + score.score[2] * 10 + + score.score[3]; +} + #endif