Add levelling

This commit is contained in:
2025-10-18 17:43:22 +01:00
parent 2078eb07da
commit 4da5437715
5 changed files with 122 additions and 38 deletions

110
game.c
View File

@@ -8,7 +8,6 @@
#include <assert.h>
#include <linux/input-event-codes.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#define SHIP_COLLIDE_R 0.05
@@ -17,14 +16,14 @@
#define FIRE_MEAN -0.15
#define FIRE_JITTER 0.01
#define INIT_LEVEL 1
#define ASTEROID_MIN_VERTS 5
#define ASTEROID_MAX_VERTS 8
#define ASTEROID_VERT_RANGE (ASTEROID_MAX_VERTS - ASTEROID_MIN_VERTS)
#define ASTEROID_A_JITTER 0.8
#define ASTEROID_VEL_JITTER 0.001
#define ASTEROID_OMG_JITTER 0.005
#define INIT_ASTEROIDS 1
#define ASTEROID_SMALL 0.05f
#define ASTEROID_MEDIUM 0.1f
#define ASTEROID_LARGE 0.2f
@@ -54,6 +53,10 @@
#define MAX_SHAPES_PER_ENTITY 2
#define MAX_COLLISIONS 128U
#define ARROW_SCALE 0.025
#define ARROW_WIDTH 4
#define ARROW_HEIGHT 4
#define COUNTER_MASK (1 << 6)
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
@@ -103,6 +106,12 @@ static const vec2_t fire_verts[] = {
};
static const vec2_t shot_verts[] = { { 0.0, -0.02 }, { 0.0, 0.02 } };
static const vec2_t arrow_verts[][MAX_VERTS] = {
{ { 1, 2 }, { 2, 0 }, { 1, -2 } },
{ { -2, 0 }, { 2, 0 } },
};
static const unsigned arrow_counts[NELEMS(arrow_verts)] = { 3, 2 };
static entity_t entities[MAX_ENTITIES];
static mat3_t transforms[MAX_ENTITIES];
static shape_t shapes[MAX_SHAPES];
@@ -110,25 +119,22 @@ static shape_t shapes[MAX_SHAPES];
static unsigned entity_count;
static unsigned shape_count;
static float aspect;
static unsigned level;
static bool dead;
static unsigned asteroid_count;
static float aspect;
static uint8_t counter;
static unsigned ship_entity_id;
static unsigned ship_shape_id;
static unsigned fire_shape_id;
static void restart()
{
game_init(aspect);
}
static entity_t *add_entity(unsigned *id_out)
{
const unsigned id = entity_count++;
memset(entities + id, 0, sizeof(entity_t));
entities[id].dir = (mat2_t) { { 1, 0 }, { 0, 1 } };
entities[id].dir = MAT2_ID;
if (id_out != nullptr)
*id_out = id;
return entities + id;
@@ -340,20 +346,28 @@ static void bounce(collision_t c)
b->vel = vec2_add(b->vel, vec2_scale(n, vb2 - vb1));
}
static void cleared()
{
renderer_set_wrap(false);
}
static void destroy_asteroid(entity_t *a, vec2_t shot_vel)
{
a->dead = true;
--asteroid_count;
float r;
if (a->radius == ASTEROID_HUGE)
if (a->radius == ASTEROID_HUGE) {
r = ASTEROID_LARGE;
else if (a->radius == ASTEROID_LARGE)
} else if (a->radius == ASTEROID_LARGE) {
r = ASTEROID_MEDIUM;
else if (a->radius == ASTEROID_MEDIUM)
} else if (a->radius == ASTEROID_MEDIUM) {
r = ASTEROID_SMALL;
else
} else {
if (asteroid_count == 0)
cleared();
return;
}
const unsigned n = REPLACE_MIN + rng_uint32() % REPLACE_RANGE;
const float da = 2 * PI / n;
@@ -383,8 +397,6 @@ static void destroy_asteroid(entity_t *a, vec2_t shot_vel)
++asteroid_count;
}
printf("asteroid count: %u\n", asteroid_count);
}
static void handle_collisions(const collision_t *collisions, unsigned count)
@@ -423,12 +435,27 @@ static void handle_collisions(const collision_t *collisions, unsigned count)
}
}
void game_init(float _aspect)
static void draw_arrow()
{
input_on_shoot(shoot);
input_on_restart(restart);
const float tx = aspect - ARROW_SCALE * (ARROW_WIDTH / 2.0 + 2);
const mat3_t m = {
{ ARROW_SCALE, 0, 0 },
{ 0, ARROW_SCALE, 0 },
{ tx, 0, 1 },
};
aspect = _aspect;
const vec3_t c_mod = { 0, 0, 1 };
const vec3_t s_mod = { ARROW_WIDTH + 2, ARROW_HEIGHT + 2, 0 };
const vec3_t c = mat3_mul_vec3(m, c_mod);
const vec3_t s = mat3_mul_vec3(m, s_mod);
renderer_clear_rect(vec3_reduce(c), (vec2_t) { s.x, s.y });
for (unsigned i = 0; i < NELEMS(arrow_verts); ++i)
renderer_draw(arrow_verts[i], arrow_counts[i], m, false);
}
static void create_field()
{
dead = false;
counter = 0;
entity_count = shape_count = 0;
@@ -450,19 +477,38 @@ void game_init(float _aspect)
fire->vert_count = NELEMS(fire_verts);
memcpy(fire->verts, fire_verts, sizeof(fire_verts));
for (unsigned i = 0; i < INIT_ASTEROIDS; ++i)
for (unsigned i = 0; i < level; ++i)
spawn_asteroid();
}
static void win()
{
renderer_set_wrap(true);
++level;
create_field();
}
static void reset()
{
level = INIT_LEVEL;
create_field();
}
void game_init(float _aspect)
{
input_on_shoot(shoot);
input_on_restart(reset);
aspect = _aspect;
reset();
}
void game_update()
{
if (dead) {
if (dead || asteroid_count == 0)
++counter;
if (dead)
return;
}
if (asteroid_count == 0 && counter <= COUNTER_MASK)
++counter;
ship_update();
@@ -472,6 +518,11 @@ void game_update()
e->dir = mat2_mul_mat2(mat2_rotation(e->omg), e->dir);
e->pos = vec2_add(e->pos, e->vel);
if (asteroid_count == 0 && i == ship_entity_id) {
if (e->pos.x > aspect)
win();
}
if (e->pos.y > 1)
e->pos.y -= 2;
else if (e->pos.y <= -1)
@@ -507,6 +558,9 @@ void game_draw()
if (dead && !(counter & COUNTER_MASK))
text_draw("GAME OVER");
if (asteroid_count == 0 && counter < COUNTER_MASK)
text_draw("CLEAR");
if (!dead && asteroid_count == 0) {
draw_arrow();
if (!(counter & COUNTER_MASK))
text_draw("CLEAR");
}
}

View File

@@ -41,6 +41,11 @@ float vec2_dot(vec2_t v1, vec2_t v2)
return v1.x * v2.x + v1.y * v2.y;
}
vec2_t vec3_reduce(vec3_t v)
{
return (vec2_t) { v.x / v.z, v.y / v.z };
}
mat2_t mat2_rotation(float theta)
{
return (mat2_t) {

View File

@@ -3,6 +3,9 @@
#define PI 3.14159265358979323846264
#define MAT2_ID ((mat2_t) { { 1, 0 }, { 0, 1 } })
#define MAT3_ID ((mat3_t) { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } })
typedef struct {
float x, y;
} vec2_t;
@@ -27,6 +30,8 @@ vec3_t vec2_extend(vec2_t v);
vec2_t vec2_norm(vec2_t v);
float vec2_dot(vec2_t v1, vec2_t v2);
vec2_t vec3_reduce(vec3_t v);
mat2_t mat2_rotation(float theta);
vec2_t mat2_mul_vec2(mat2_t m, vec2_t v);
mat2_t mat2_mul_mat2(mat2_t m1, mat2_t m2);

View File

@@ -20,6 +20,8 @@ static int front, back;
static uint32_t width, height;
static mat3_t view;
static bool wrap;
static vec3_t vert_buf[MAX_VERTS];
static void page_flip_handler(int, unsigned, unsigned, unsigned, void *)
@@ -34,6 +36,13 @@ static void set_pixel(uint32_t x, uint32_t y)
static void draw_line(vec3_t v1, vec3_t v2)
{
v1.x /= v1.z;
v1.y /= v1.z;
v1.z = 1;
v2.x /= v2.z;
v2.y /= v2.z;
v2.z = 1;
const float delta_x = v2.x - v1.x;
const float delta_y = v2.y - v1.y;
const float step = fmaxf(fabsf(delta_x), fabsf(delta_y));
@@ -43,16 +52,20 @@ static void draw_line(vec3_t v1, vec3_t v2)
for (unsigned i = 0; i <= step; ++i) {
float x = roundf(v1.x + i * step_x);
if (x >= width)
x -= width;
else if (x < 0)
x += width;
float y = roundf(v1.y + i * step_y);
if (y >= height)
y -= height;
else if (y < 0)
y += height;
if (wrap) {
if (x >= width)
x -= width;
else if (x < 0)
x += width;
if (y >= height)
y -= height;
else if (y < 0)
y += height;
} else if (x >= width || x < 0 || y >= height || y < 0) {
continue;
}
set_pixel(x, y);
}
@@ -89,6 +102,8 @@ renderer_params_t renderer_init()
width = mode.hdisplay;
height = mode.vdisplay;
wrap = true;
const float aspect = (float)width / (float)height;
const float scale = (float)height / 2.0f;
view = (mat3_t) {
@@ -136,6 +151,11 @@ void renderer_swap()
back = (back + 1) & 1;
}
void renderer_set_wrap(bool enable)
{
wrap = enable;
}
void renderer_clear()
{
memset(fbs[back].buf, 0, fbs[back].size);

View File

@@ -18,9 +18,9 @@ void renderer_cleanup();
void renderer_handle();
void renderer_swap();
void renderer_set_wrap(bool enable);
void renderer_clear();
void renderer_clear_rect(vec2_t centre, vec2_t size);
void renderer_draw(
const vec2_t *vs, unsigned count, mat3_t model, bool connect);