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 <assert.h>
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#include <math.h> #include <math.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#define SHIP_COLLIDE_R 0.05 #define SHIP_COLLIDE_R 0.05
@@ -17,14 +16,14 @@
#define FIRE_MEAN -0.15 #define FIRE_MEAN -0.15
#define FIRE_JITTER 0.01 #define FIRE_JITTER 0.01
#define INIT_LEVEL 1
#define ASTEROID_MIN_VERTS 5 #define ASTEROID_MIN_VERTS 5
#define ASTEROID_MAX_VERTS 8 #define ASTEROID_MAX_VERTS 8
#define ASTEROID_VERT_RANGE (ASTEROID_MAX_VERTS - ASTEROID_MIN_VERTS) #define ASTEROID_VERT_RANGE (ASTEROID_MAX_VERTS - ASTEROID_MIN_VERTS)
#define ASTEROID_A_JITTER 0.8 #define ASTEROID_A_JITTER 0.8
#define ASTEROID_VEL_JITTER 0.001 #define ASTEROID_VEL_JITTER 0.001
#define ASTEROID_OMG_JITTER 0.005 #define ASTEROID_OMG_JITTER 0.005
#define INIT_ASTEROIDS 1
#define ASTEROID_SMALL 0.05f #define ASTEROID_SMALL 0.05f
#define ASTEROID_MEDIUM 0.1f #define ASTEROID_MEDIUM 0.1f
#define ASTEROID_LARGE 0.2f #define ASTEROID_LARGE 0.2f
@@ -54,6 +53,10 @@
#define MAX_SHAPES_PER_ENTITY 2 #define MAX_SHAPES_PER_ENTITY 2
#define MAX_COLLISIONS 128U #define MAX_COLLISIONS 128U
#define ARROW_SCALE 0.025
#define ARROW_WIDTH 4
#define ARROW_HEIGHT 4
#define COUNTER_MASK (1 << 6) #define COUNTER_MASK (1 << 6)
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0])) #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 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 entity_t entities[MAX_ENTITIES];
static mat3_t transforms[MAX_ENTITIES]; static mat3_t transforms[MAX_ENTITIES];
static shape_t shapes[MAX_SHAPES]; static shape_t shapes[MAX_SHAPES];
@@ -110,25 +119,22 @@ static shape_t shapes[MAX_SHAPES];
static unsigned entity_count; static unsigned entity_count;
static unsigned shape_count; static unsigned shape_count;
static float aspect;
static unsigned level;
static bool dead; static bool dead;
static unsigned asteroid_count; static unsigned asteroid_count;
static float aspect;
static uint8_t counter; static uint8_t counter;
static unsigned ship_entity_id; static unsigned ship_entity_id;
static unsigned ship_shape_id; static unsigned ship_shape_id;
static unsigned fire_shape_id; static unsigned fire_shape_id;
static void restart()
{
game_init(aspect);
}
static entity_t *add_entity(unsigned *id_out) static entity_t *add_entity(unsigned *id_out)
{ {
const unsigned id = entity_count++; const unsigned id = entity_count++;
memset(entities + id, 0, sizeof(entity_t)); 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) if (id_out != nullptr)
*id_out = id; *id_out = id;
return entities + 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)); 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) static void destroy_asteroid(entity_t *a, vec2_t shot_vel)
{ {
a->dead = true; a->dead = true;
--asteroid_count; --asteroid_count;
float r; float r;
if (a->radius == ASTEROID_HUGE) if (a->radius == ASTEROID_HUGE) {
r = ASTEROID_LARGE; r = ASTEROID_LARGE;
else if (a->radius == ASTEROID_LARGE) } else if (a->radius == ASTEROID_LARGE) {
r = ASTEROID_MEDIUM; r = ASTEROID_MEDIUM;
else if (a->radius == ASTEROID_MEDIUM) } else if (a->radius == ASTEROID_MEDIUM) {
r = ASTEROID_SMALL; r = ASTEROID_SMALL;
else } else {
if (asteroid_count == 0)
cleared();
return; return;
}
const unsigned n = REPLACE_MIN + rng_uint32() % REPLACE_RANGE; const unsigned n = REPLACE_MIN + rng_uint32() % REPLACE_RANGE;
const float da = 2 * PI / n; const float da = 2 * PI / n;
@@ -383,8 +397,6 @@ static void destroy_asteroid(entity_t *a, vec2_t shot_vel)
++asteroid_count; ++asteroid_count;
} }
printf("asteroid count: %u\n", asteroid_count);
} }
static void handle_collisions(const collision_t *collisions, unsigned 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); const float tx = aspect - ARROW_SCALE * (ARROW_WIDTH / 2.0 + 2);
input_on_restart(restart); 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; dead = false;
counter = 0; counter = 0;
entity_count = shape_count = 0; entity_count = shape_count = 0;
@@ -450,19 +477,38 @@ void game_init(float _aspect)
fire->vert_count = NELEMS(fire_verts); fire->vert_count = NELEMS(fire_verts);
memcpy(fire->verts, fire_verts, sizeof(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(); 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() void game_update()
{ {
if (dead) { if (dead || asteroid_count == 0)
++counter; ++counter;
if (dead)
return; return;
}
if (asteroid_count == 0 && counter <= COUNTER_MASK)
++counter;
ship_update(); ship_update();
@@ -472,6 +518,11 @@ void game_update()
e->dir = mat2_mul_mat2(mat2_rotation(e->omg), e->dir); e->dir = mat2_mul_mat2(mat2_rotation(e->omg), e->dir);
e->pos = vec2_add(e->pos, e->vel); 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) if (e->pos.y > 1)
e->pos.y -= 2; e->pos.y -= 2;
else if (e->pos.y <= -1) else if (e->pos.y <= -1)
@@ -507,6 +558,9 @@ void game_draw()
if (dead && !(counter & COUNTER_MASK)) if (dead && !(counter & COUNTER_MASK))
text_draw("GAME OVER"); text_draw("GAME OVER");
if (asteroid_count == 0 && counter < COUNTER_MASK) if (!dead && asteroid_count == 0) {
text_draw("CLEAR"); 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; 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) mat2_t mat2_rotation(float theta)
{ {
return (mat2_t) { return (mat2_t) {

View File

@@ -3,6 +3,9 @@
#define PI 3.14159265358979323846264 #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 { typedef struct {
float x, y; float x, y;
} vec2_t; } vec2_t;
@@ -27,6 +30,8 @@ vec3_t vec2_extend(vec2_t v);
vec2_t vec2_norm(vec2_t v); vec2_t vec2_norm(vec2_t v);
float vec2_dot(vec2_t v1, vec2_t v2); float vec2_dot(vec2_t v1, vec2_t v2);
vec2_t vec3_reduce(vec3_t v);
mat2_t mat2_rotation(float theta); mat2_t mat2_rotation(float theta);
vec2_t mat2_mul_vec2(mat2_t m, vec2_t v); vec2_t mat2_mul_vec2(mat2_t m, vec2_t v);
mat2_t mat2_mul_mat2(mat2_t m1, mat2_t m2); 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 uint32_t width, height;
static mat3_t view; static mat3_t view;
static bool wrap;
static vec3_t vert_buf[MAX_VERTS]; static vec3_t vert_buf[MAX_VERTS];
static void page_flip_handler(int, unsigned, unsigned, unsigned, void *) 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) 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_x = v2.x - v1.x;
const float delta_y = v2.y - v1.y; const float delta_y = v2.y - v1.y;
const float step = fmaxf(fabsf(delta_x), fabsf(delta_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) { for (unsigned i = 0; i <= step; ++i) {
float x = roundf(v1.x + i * step_x); 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); float y = roundf(v1.y + i * step_y);
if (y >= height)
y -= height; if (wrap) {
else if (y < 0) if (x >= width)
y += height; 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); set_pixel(x, y);
} }
@@ -89,6 +102,8 @@ renderer_params_t renderer_init()
width = mode.hdisplay; width = mode.hdisplay;
height = mode.vdisplay; height = mode.vdisplay;
wrap = true;
const float aspect = (float)width / (float)height; const float aspect = (float)width / (float)height;
const float scale = (float)height / 2.0f; const float scale = (float)height / 2.0f;
view = (mat3_t) { view = (mat3_t) {
@@ -136,6 +151,11 @@ void renderer_swap()
back = (back + 1) & 1; back = (back + 1) & 1;
} }
void renderer_set_wrap(bool enable)
{
wrap = enable;
}
void renderer_clear() void renderer_clear()
{ {
memset(fbs[back].buf, 0, fbs[back].size); memset(fbs[back].buf, 0, fbs[back].size);

View File

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