From a1cfb77cac2dbddd389fd328d7a7bcc4f78a33fa Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Wed, 15 Oct 2025 18:37:21 +0100 Subject: [PATCH] Add levelling --- game.c | 110 +++++++++++++++++++++++++++++++++++++++-------------- maths.c | 5 +++ maths.h | 5 +++ renderer.c | 38 +++++++++++++----- renderer.h | 2 +- 5 files changed, 122 insertions(+), 38 deletions(-) diff --git a/game.c b/game.c index 2434579..7131078 100644 --- a/game.c +++ b/game.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #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"); + } } diff --git a/maths.c b/maths.c index d17e448..6a4091d 100644 --- a/maths.c +++ b/maths.c @@ -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) { diff --git a/maths.h b/maths.h index 827aaf1..47e083b 100644 --- a/maths.h +++ b/maths.h @@ -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); diff --git a/renderer.c b/renderer.c index ae749db..6137f09 100644 --- a/renderer.c +++ b/renderer.c @@ -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); diff --git a/renderer.h b/renderer.h index 758bbb5..8b6dcb4 100644 --- a/renderer.h +++ b/renderer.h @@ -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);