2024-04-07 18:59:30 +02:00
|
|
|
|
|
|
|
#define SSD1306_128X32
|
|
|
|
#include "ch32v003fun.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "ssd1306_i2c.h"
|
|
|
|
#include "ssd1306.h"
|
|
|
|
|
|
|
|
#include "data.h"
|
|
|
|
|
|
|
|
#define PIN_LED PC4
|
|
|
|
#define PIN_BTN PC7
|
|
|
|
|
|
|
|
// #define JUMP_POWER 48
|
|
|
|
// #define GRAVITY 3
|
|
|
|
#define JUMP_POWER 36
|
|
|
|
#define GRAVITY 2
|
|
|
|
#define SCROLL_SPEED 1
|
|
|
|
#define FRAME_DELAY 1000 / 60
|
|
|
|
#define SCORE_DIGITS 4
|
|
|
|
#define RUN_ANIM_SPEED 4
|
|
|
|
#define PLAYER_X 16
|
|
|
|
#define Y_PRECISION 4
|
|
|
|
|
|
|
|
u32 time = 0;
|
|
|
|
u16 score = 0;
|
|
|
|
u16 highscore = 0;
|
|
|
|
s16 player_y = 0;
|
|
|
|
s16 player_vel = 0;
|
|
|
|
#define MAX_OBSTACLES 16
|
|
|
|
u8 obstacle_positions[MAX_OBSTACLES];
|
|
|
|
u8 obstacle_types[MAX_OBSTACLES]; // todo
|
|
|
|
u8 obstacle_count = 0;
|
|
|
|
u8 is_running = 1;
|
|
|
|
u8 time_to_obstacle = 0;
|
|
|
|
|
|
|
|
uint32_t lfsr = 1; // PRNG state
|
|
|
|
|
|
|
|
u8 rand8();
|
|
|
|
void update();
|
|
|
|
void render();
|
|
|
|
void restart();
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
SystemInit();
|
|
|
|
funGpioInitAll();
|
|
|
|
funPinMode(PIN_LED, GPIO_CFGLR_OUT_10Mhz_PP);
|
|
|
|
funPinMode(PIN_BTN, GPIO_CFGLR_IN_PUPD);
|
|
|
|
|
|
|
|
ssd1306_i2c_init();
|
|
|
|
ssd1306_init();
|
|
|
|
|
|
|
|
restart();
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
ssd1306_refresh();
|
|
|
|
|
|
|
|
if (is_running)
|
|
|
|
update();
|
|
|
|
render();
|
|
|
|
time += 1;
|
|
|
|
|
|
|
|
if (funDigitalRead(PIN_BTN))
|
|
|
|
{
|
|
|
|
if (is_running)
|
|
|
|
{
|
|
|
|
if (player_y == 0 && player_vel == 0)
|
|
|
|
{
|
|
|
|
player_vel = JUMP_POWER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
restart();
|
|
|
|
}
|
|
|
|
if (is_running == 0)
|
|
|
|
{
|
|
|
|
funDigitalWrite(PIN_LED, (time >> 2) & 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
funDigitalWrite(PIN_LED, FUN_LOW);
|
|
|
|
}
|
|
|
|
Delay_Ms(FRAME_DELAY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void restart()
|
|
|
|
{
|
|
|
|
lfsr = SysTick->CNT;
|
|
|
|
is_running = 1;
|
|
|
|
player_vel = 0;
|
|
|
|
player_y = 0;
|
|
|
|
obstacle_count = 0;
|
|
|
|
time_to_obstacle = 0;
|
|
|
|
if (highscore < score)
|
|
|
|
highscore = score;
|
|
|
|
score = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_obstacle()
|
|
|
|
{
|
|
|
|
if (obstacle_count == 0)
|
|
|
|
return;
|
|
|
|
for (u8 i = 1; i < obstacle_count; i++)
|
|
|
|
{
|
|
|
|
obstacle_positions[i - 1] = obstacle_positions[i];
|
|
|
|
obstacle_types[i - 1] = obstacle_types[i];
|
|
|
|
}
|
|
|
|
obstacle_count--;
|
|
|
|
}
|
|
|
|
|
2024-04-07 19:35:35 +02:00
|
|
|
void spawn_ground_obstacle(u8 pos)
|
2024-04-07 18:59:30 +02:00
|
|
|
{
|
|
|
|
if (obstacle_count >= MAX_OBSTACLES)
|
|
|
|
return;
|
|
|
|
obstacle_positions[obstacle_count] = pos;
|
2024-04-07 19:35:35 +02:00
|
|
|
obstacle_types[obstacle_count] = rand8() % GROUND_OBSTACLE_TYPES;
|
2024-04-07 18:59:30 +02:00
|
|
|
obstacle_count++;
|
|
|
|
}
|
2024-04-07 19:35:35 +02:00
|
|
|
void spawn_air_obstacle(u8 pos)
|
|
|
|
{
|
|
|
|
spawn_ground_obstacle(pos);
|
|
|
|
obstacle_types[obstacle_count - 1] = 5;
|
|
|
|
}
|
2024-04-07 18:59:30 +02:00
|
|
|
|
|
|
|
void update()
|
|
|
|
{
|
|
|
|
score += (time & 4) != 0;
|
|
|
|
if (player_vel != 0 || player_y != 0)
|
|
|
|
{
|
|
|
|
player_vel -= GRAVITY;
|
|
|
|
player_y += player_vel;
|
|
|
|
if (player_y < 0)
|
|
|
|
{
|
|
|
|
player_y = 0;
|
|
|
|
player_vel = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (u8 i = 0; i < obstacle_count; i++)
|
|
|
|
{
|
|
|
|
obstacle_positions[i] -= SCROLL_SPEED;
|
|
|
|
u8 type = obstacle_types[i];
|
|
|
|
u8 dist_x = gfx_abs((s16)obstacle_positions[i] - (s16)PLAYER_X);
|
2024-04-07 19:35:35 +02:00
|
|
|
u8 dist_y = gfx_abs((s16)obstacle_y_offset[type] - (s16)(player_y >> Y_PRECISION));
|
|
|
|
|
|
|
|
if (dist_x < obstacle_widths[type] && dist_y < obstacle_heights[type])
|
2024-04-07 18:59:30 +02:00
|
|
|
{
|
|
|
|
is_running = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (obstacle_count > 0 && obstacle_positions[0] == 0)
|
|
|
|
{
|
|
|
|
remove_obstacle();
|
|
|
|
}
|
2024-04-07 19:35:35 +02:00
|
|
|
if (time_to_obstacle == 0)
|
2024-04-07 18:59:30 +02:00
|
|
|
{
|
2024-04-07 19:35:35 +02:00
|
|
|
if (rand8() & rand8() & 1)
|
|
|
|
spawn_air_obstacle(128);
|
|
|
|
else
|
2024-04-07 18:59:30 +02:00
|
|
|
{
|
2024-04-07 19:35:35 +02:00
|
|
|
spawn_ground_obstacle(128);
|
|
|
|
if (rand8() & rand8() & 1 && obstacle_types[obstacle_count - 1] != 5)
|
|
|
|
spawn_ground_obstacle(136);
|
2024-04-07 18:59:30 +02:00
|
|
|
}
|
2024-04-07 19:35:35 +02:00
|
|
|
|
|
|
|
time_to_obstacle = (rand8() & 31) + 45;
|
2024-04-07 18:59:30 +02:00
|
|
|
}
|
|
|
|
time_to_obstacle -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void render_digit(u8 digit, u8 x, u8 y)
|
|
|
|
{
|
|
|
|
u16 pixels = digit_font[digit];
|
|
|
|
ssd1306_drawPixel(x + 0, y + 0, (pixels >> 14) & 1);
|
|
|
|
ssd1306_drawPixel(x + 1, y + 0, (pixels >> 13) & 1);
|
|
|
|
ssd1306_drawPixel(x + 2, y + 0, (pixels >> 12) & 1);
|
|
|
|
ssd1306_drawPixel(x + 0, y + 1, (pixels >> 11) & 1);
|
|
|
|
ssd1306_drawPixel(x + 1, y + 1, (pixels >> 10) & 1);
|
|
|
|
ssd1306_drawPixel(x + 2, y + 1, (pixels >> 9) & 1);
|
|
|
|
ssd1306_drawPixel(x + 0, y + 2, (pixels >> 8) & 1);
|
|
|
|
ssd1306_drawPixel(x + 1, y + 2, (pixels >> 7) & 1);
|
|
|
|
ssd1306_drawPixel(x + 2, y + 2, (pixels >> 6) & 1);
|
|
|
|
ssd1306_drawPixel(x + 0, y + 3, (pixels >> 5) & 1);
|
|
|
|
ssd1306_drawPixel(x + 1, y + 3, (pixels >> 4) & 1);
|
|
|
|
ssd1306_drawPixel(x + 2, y + 3, (pixels >> 3) & 1);
|
|
|
|
ssd1306_drawPixel(x + 0, y + 4, (pixels >> 2) & 1);
|
|
|
|
ssd1306_drawPixel(x + 1, y + 4, (pixels >> 1) & 1);
|
|
|
|
ssd1306_drawPixel(x + 2, y + 4, (pixels >> 0) & 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void render_number(u32 s, u8 x, u8 y, u8 digits)
|
|
|
|
{
|
|
|
|
u8 painter_x = x + digits * 4;
|
|
|
|
u32 val = s;
|
|
|
|
|
|
|
|
for (int i = 0; i < digits; i++)
|
|
|
|
{
|
|
|
|
u8 digit = val % 10;
|
|
|
|
render_digit(digit, painter_x, y);
|
|
|
|
val /= 10;
|
|
|
|
painter_x -= 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void render()
|
|
|
|
{
|
|
|
|
ssd1306_setbuf(0);
|
|
|
|
ssd1306_drawFastHLine(0, 31, 128, 1);
|
|
|
|
|
|
|
|
render_number(score, 0, 0, SCORE_DIGITS);
|
|
|
|
render_number(highscore, 0, 6, SCORE_DIGITS);
|
|
|
|
// render_number(obstacle_count, 20, 0, 2);
|
|
|
|
// render_number(abs(player_vel), 30, 0, 2);
|
|
|
|
// render_number(time_to_obstacle, 20, 6, 3);
|
|
|
|
|
|
|
|
for (u8 i = 0; i < obstacle_count; i++)
|
|
|
|
{
|
|
|
|
u8 type = obstacle_types[i];
|
2024-04-07 19:35:35 +02:00
|
|
|
ssd1306_drawImage(obstacle_positions[i], 31 - 8 - obstacle_y_offset[type], obstacle_sprites[type], 8, 8, 0);
|
2024-04-07 18:59:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (time & RUN_ANIM_SPEED && is_running)
|
2024-04-07 19:35:35 +02:00
|
|
|
ssd1306_drawImage(PLAYER_X, 31 - 8 - (player_y >> Y_PRECISION), soweli_a, 8, 8, 0);
|
2024-04-07 18:59:30 +02:00
|
|
|
else
|
2024-04-07 19:35:35 +02:00
|
|
|
ssd1306_drawImage(PLAYER_X, 31 - 8 - (player_y >> Y_PRECISION), soweli_b, 8, 8, 0);
|
2024-04-07 18:59:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* White Noise Generator State */
|
|
|
|
#define NOISE_BITS 8
|
|
|
|
#define NOISE_MASK ((1 << NOISE_BITS) - 1)
|
|
|
|
#define NOISE_POLY_TAP0 31
|
|
|
|
#define NOISE_POLY_TAP1 21
|
|
|
|
#define NOISE_POLY_TAP2 1
|
|
|
|
#define NOISE_POLY_TAP3 0
|
|
|
|
/*
|
|
|
|
* random byte generator, taken from ch32v003fun examples
|
|
|
|
*/
|
|
|
|
uint8_t rand8(void)
|
|
|
|
{
|
|
|
|
uint8_t bit;
|
|
|
|
uint32_t new_data;
|
|
|
|
|
|
|
|
for (bit = 0; bit < NOISE_BITS; bit++)
|
|
|
|
{
|
|
|
|
new_data = ((lfsr >> NOISE_POLY_TAP0) ^
|
|
|
|
(lfsr >> NOISE_POLY_TAP1) ^
|
|
|
|
(lfsr >> NOISE_POLY_TAP2) ^
|
|
|
|
(lfsr >> NOISE_POLY_TAP3));
|
|
|
|
lfsr = (lfsr << 1) | (new_data & 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return lfsr & NOISE_MASK;
|
|
|
|
}
|