ch32_oled/soweli/soweli.c
2024-04-07 18:59:30 +02:00

255 lines
No EOL
5.4 KiB
C

#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--;
}
void spawn_obstacle(u8 pos)
{
if (obstacle_count >= MAX_OBSTACLES)
return;
obstacle_positions[obstacle_count] = pos;
obstacle_types[obstacle_count] = rand8() % OBSTACLE_TYPES;
obstacle_count++;
}
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);
if (dist_x < obstacle_widths[type] && (player_y >> Y_PRECISION) < obstacle_heights[type])
{
is_running = 0;
}
}
if (obstacle_count > 0 && obstacle_positions[0] == 0)
{
remove_obstacle();
}
if (time_to_obstacle < 5)
{
u8 count = 1 + (rand8() & rand8() & 1) + (rand8() & rand8() & rand8() & rand8() & 1);
for (u8 i = 0; i < count; i++)
{
spawn_obstacle(128 + i * 8);
}
time_to_obstacle = (rand8() & 31) + 50;
}
time_to_obstacle -= 1;
}
void render_digit(u8 digit, u8 x, u8 y)
{
const u16 digit_font[] = {
0b111101101101111, // 0
0b010110010010111, // 1
0b111001111100111, // 2
0b111001111001111, // 3
0b101101111001001, // 4
0b111100111001111, // 5
0b111100111101111, // 6
0b111001001001001, // 7
0b111101111101111, // 8
0b111101111001111, // 9
};
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];
ssd1306_drawImage(obstacle_positions[i], 31 - 8, obstacle_sprites[type], 8, 8, 0);
}
if (time & RUN_ANIM_SPEED && is_running)
ssd1306_drawImage(PLAYER_X, 31 - (player_y >> Y_PRECISION) - 8, soweli_a, 8, 8, 0);
else
ssd1306_drawImage(PLAYER_X, 31 - (player_y >> Y_PRECISION) - 8, soweli_b, 8, 8, 0);
}
/* 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;
}