Compare commits

...

4 Commits

7 changed files with 111 additions and 39 deletions

6
README
View File

@@ -3,8 +3,10 @@
This is a clone (ish) of the classic game Asteroids, written in C23 This is a clone (ish) of the classic game Asteroids, written in C23
for Linux. To build the program, you'll need the Linux headers, a for Linux. To build the program, you'll need the Linux headers, a
C23-compatible compiler and libdrm, along with its headers. There is C23-compatible compiler and libdrm, along with its headers. There is
a build script that uses GCC; simply running this (`./build.sh`) is a build script that uses GCC; simply running this (./build.sh) is
likely to work on many systems. likely to work on many systems. However, you will likely need to
adjust the input and video devices used (in input.c and renderer.c
respectively) to match your system.
The game directly uses Linux DRM, so it doesn't play nice with X, The game directly uses Linux DRM, so it doesn't play nice with X,
display managers etc. You'll likely want to change to a virtual TTY display managers etc. You'll likely want to change to a virtual TTY

View File

@@ -13,9 +13,9 @@
#define MIN_VERTS 5 #define MIN_VERTS 5
#define VERT_RANGE (MAX_VERTS - MIN_VERTS) #define VERT_RANGE (MAX_VERTS - MIN_VERTS)
#define A_JITTER 0.8 #define A_JITTER 0.8
#define V_JITTER 0.001 #define V_JITTER 0.0005
#define ROT_JITTER 0.005 #define ROT_JITTER 0.005
#define R_JITTER 0.02 #define R_JITTER 0.01
#define MIN_DIST 0.8 #define MIN_DIST 0.8
#define REPLACE_MIN 3 #define REPLACE_MIN 3
@@ -41,10 +41,10 @@ static unsigned count;
static entry_t entries[MAX_ENTITIES]; static entry_t entries[MAX_ENTITIES];
static float radii[] = { static float radii[] = {
[ASTEROID_SMALL] = 0.05f, [ASTEROID_SMALL] = 0.025f,
[ASTEROID_MEDIUM] = 0.1f, [ASTEROID_MEDIUM] = 0.05f,
[ASTEROID_LARGE] = 0.2f, [ASTEROID_LARGE] = 0.1f,
[ASTEROID_HUGE] = 0.4f, [ASTEROID_HUGE] = 0.2f,
}; };
static void update(unsigned new_entity_id, void *ref) static void update(unsigned new_entity_id, void *ref)

View File

@@ -9,4 +9,5 @@ defs="-D_POSIX_C_SOURCE=200809L"
$cc $warn $flags $libs $defs \ $cc $warn $flags $libs $defs \
-o asteroids \ -o asteroids \
asteroids.c collisions.c entity.c fb.c game.c input.c \ asteroids.c collisions.c entity.c fb.c game.c input.c \
main.c maths.c physics.c scene.c renderer.c rng.c text.c main.c maths.c physics.c scene.c renderer.c rng.c \
self_destruct.c text.c

59
game.c
View File

@@ -8,6 +8,7 @@
#include "renderer.h" #include "renderer.h"
#include "rng.h" #include "rng.h"
#include "scene.h" #include "scene.h"
#include "self_destruct.h"
#include "text.h" #include "text.h"
#include <assert.h> #include <assert.h>
@@ -15,32 +16,34 @@
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#define FPS 60
#define INIT_LEVEL 1 #define INIT_LEVEL 1
#define SHIP_COLLIDE_R 0.05 #define SHIP_COLLIDE_R 0.025
#define SHIP_MASS 0.02 #define SHIP_MASS 0.01
#define FIRE_MEAN -0.15 #define FIRE_JITTER 0.005
#define FIRE_JITTER 0.01
#define LIN_PWR 0.0001 #define LIN_PWR 0.00005
#define ROT_PWR 0.002 #define ROT_PWR 0.002
#define SHOT_VEL 0.03 #define SHOT_VEL 0.03
#define SHOT_COLLIDE_R 0.03 #define SHOT_COLLIDE_R 0.015
#define SHOT_MASS 0.001 #define SHOT_MASS 0.00025
#define SHOT_LIFETIME_S 4
#define ARROW_SCALE 0.025 #define ARROW_SCALE 0.0125
#define ARROW_WIDTH 4 #define ARROW_WIDTH 4
#define ARROW_HEIGHT 4 #define ARROW_HEIGHT 4
#define COUNTER_MASK (1 << 6) #define COUNTER_MASK (1 << 6)
#define ITEM_SPAWN_R 0.9 #define ITEM_SPAWN_R 0.9
#define ITEM_COLLIDE_R 0.025 #define ITEM_COLLIDE_R 0.0125
#define ITEM_SPAWN_EXP_S 40 #define ITEM_SPAWN_EXP_S 40
#define ITEM_MIN_SCORE 32 #define ITEM_MIN_SCORE 32
#define ARMOUR_COEFF 5e-6 #define ARMOUR_COEFF 2.5e-6
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0])) #define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
@@ -51,17 +54,17 @@ enum {
}; };
static const vec2_t ship_verts[] = { static const vec2_t ship_verts[] = {
{ 0.0, 0.11 }, { 0.0, 0.055 },
{ 0.05, -0.07 }, { 0.025, -0.035 },
{ 0.0, -0.04 }, { 0.0, -0.02 },
{ -0.05, -0.07 }, { -0.025, -0.035 },
}; };
static const vec2_t fire_verts[] = { static const vec2_t fire_verts[] = {
{ 0.0, -0.15 }, { 0.0, -0.075 },
{ 0.015, -0.07 }, { 0.0075, -0.035 },
{ -0.015, -0.07 }, { -0.0075, -0.035 },
}; };
static const vec2_t shot_verts[] = { { 0.0, -0.02 }, { 0.0, 0.02 } }; static const vec2_t shot_verts[] = { { 0.0, -0.01 }, { 0.0, 0.01 } };
static const vec2_t arrow_verts[][MAX_VERTS] = { static const vec2_t arrow_verts[][MAX_VERTS] = {
{ { 1, 2 }, { 2, 0 }, { 1, -2 } }, { { 1, 2 }, { 2, 0 }, { 1, -2 } },
@@ -70,8 +73,8 @@ static const vec2_t arrow_verts[][MAX_VERTS] = {
static const unsigned arrow_counts[NELEMS(arrow_verts)] = { 3, 2 }; static const unsigned arrow_counts[NELEMS(arrow_verts)] = { 3, 2 };
static const vec2_t item_verts[][MAX_VERTS] = { static const vec2_t item_verts[][MAX_VERTS] = {
{ { -0.025, -0.025 }, { 0.025, 0.025 } }, { { -0.0125, -0.0125 }, { 0.0125, 0.0125 } },
{ { -0.025, 0.025 }, { 0.025, -0.025 } }, { { -0.0125, 0.0125 }, { 0.0125, -0.0125 } },
}; };
static const unsigned item_counts[NELEMS(item_verts)] = { 2, 2 }; static const unsigned item_counts[NELEMS(item_verts)] = { 2, 2 };
@@ -175,6 +178,7 @@ static void shoot()
scene_add(id, shot_verts, NELEMS(shot_verts), false); scene_add(id, shot_verts, NELEMS(shot_verts), false);
physics_add(id, pos, ship->dir, vel, 0, SHOT_MASS); physics_add(id, pos, ship->dir, vel, 0, SHOT_MASS);
collisions_add(id, SHOT_COLLIDE_R, SHOT_COLLIDE_PRIOR, shot_collide); collisions_add(id, SHOT_COLLIDE_R, SHOT_COLLIDE_PRIOR, shot_collide);
self_destruct_add(id, FPS * SHOT_LIFETIME_S);
const vec2_t p_ship = vec2_scale(ship->vel, ship->mass); const vec2_t p_ship = vec2_scale(ship->vel, ship->mass);
const vec2_t p_shot = vec2_scale(vel, SHOT_MASS); const vec2_t p_shot = vec2_scale(vel, SHOT_MASS);
@@ -190,19 +194,13 @@ static void draw_arrow()
{ tx, 0, 1 }, { tx, 0, 1 },
}; };
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) for (unsigned i = 0; i < NELEMS(arrow_verts); ++i)
renderer_draw(arrow_verts[i], arrow_counts[i], m, false); renderer_draw(arrow_verts[i], arrow_counts[i], m, false);
} }
static void anim_fire(vec2_t *verts) static void anim_fire(vec2_t *verts)
{ {
verts[0].y = FIRE_MEAN + FIRE_JITTER * rng_plusminus(); verts[0].y = fire_verts[0].y + FIRE_JITTER * rng_plusminus();
} }
static void create_field() static void create_field()
@@ -220,6 +218,7 @@ static void create_field()
scene_clear(); scene_clear();
physics_init(); physics_init();
collisions_init(); collisions_init();
self_destruct_clear();
ship_entity_id = entity_add(); ship_entity_id = entity_add();
scene_add(ship_entity_id, ship_verts, NELEMS(ship_verts), true); scene_add(ship_entity_id, ship_verts, NELEMS(ship_verts), true);
@@ -316,14 +315,16 @@ void game_update()
ship_update(); ship_update();
physics_update(); physics_update();
self_destruct_update();
collisions_update(); collisions_update();
scene_update(); scene_update();
entities_purge(); entities_purge();
const bool items_enabled = score >= ITEM_MIN_SCORE; const bool items_enabled
= !clear && score >= ITEM_MIN_SCORE && armour_level < 9;
if (items_enabled && !item if (items_enabled && !item
&& rng_canon() < 1.0f / (60 * ITEM_SPAWN_EXP_S)) && rng_canon() < 1.0f / (FPS * ITEM_SPAWN_EXP_S))
add_item(); add_item();
} }

60
self_destruct.c Normal file
View File

@@ -0,0 +1,60 @@
#include "self_destruct.h"
#include "entity.h"
#include <assert.h>
#include <string.h>
#define MAX 256U
typedef struct {
unsigned entity_id;
unsigned component_id;
unsigned rem;
} cmp_t;
static unsigned count;
static cmp_t cmps[MAX];
static void update(unsigned new_entity_id, void *ref)
{
cmp_t *c = (cmp_t *)ref;
c->entity_id = new_entity_id;
}
static void remove(void *ref)
{
cmp_t *c = (cmp_t *)ref;
const cmp_t *last = cmps + (count - 1);
if (c < last) {
memcpy(c, last, sizeof(cmp_t));
entity_update_component(c->entity_id, c->component_id, c);
}
--count;
}
void self_destruct_clear()
{
count = 0;
}
void self_destruct_update()
{
for (unsigned i = 0; i < count; ++i) {
if (cmps[i].rem == 0)
entity_mark(cmps[i].entity_id);
else
--cmps[i].rem;
}
}
void self_destruct_add(unsigned id, unsigned frames)
{
assert(count < MAX);
cmp_t *c = cmps + count++;
*c = (cmp_t) {
.entity_id = id,
.rem = frames,
.component_id = entity_add_component(id, update, remove, c),
};
}

8
self_destruct.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef SELF_DESTRUCT_H
#define SELF_DESTRUCT_H
void self_destruct_clear();
void self_destruct_update();
void self_destruct_add(unsigned id, unsigned frames);
#endif

2
text.c
View File

@@ -9,7 +9,7 @@
#define MAX_LINES 2 #define MAX_LINES 2
#define TEXT_HEIGHT 8 #define TEXT_HEIGHT 8
#define TEXT_SCALE 0.025 #define TEXT_SCALE 0.0125
#define LETTER_WIDTH 3 #define LETTER_WIDTH 3
#define SPACE_WIDTH 1 #define SPACE_WIDTH 1