Add armour items

This commit is contained in:
Camden Dixie O'Brien
2025-10-18 10:58:41 +01:00
parent 74113c32b4
commit 311dd4322d

93
game.c
View File

@@ -35,11 +35,19 @@
#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_COLLIDE_R 0.025
#define ITEM_SPAWN_EXP_S 40
#define ITEM_MIN_SCORE 32
#define ARMOUR_COEFF 5e-6
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0])) #define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
enum { enum {
SHOT_COLLIDE_PRIOR = 1, SHOT_COLLIDE_PRIOR = 1,
SHIP_COLLIDE_PRIOR, SHIP_COLLIDE_PRIOR,
ITEM_COLLIDE_PRIOR,
}; };
static const vec2_t ship_verts[] = { static const vec2_t ship_verts[] = {
@@ -61,6 +69,12 @@ 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] = {
{ { -0.025, -0.025 }, { 0.025, 0.025 } },
{ { -0.025, 0.025 }, { 0.025, -0.025 } },
};
static const unsigned item_counts[NELEMS(item_verts)] = { 2, 2 };
static const vec2_t shot_vel = { 0, SHOT_VEL }; static const vec2_t shot_vel = { 0, SHOT_VEL };
static const asteroid_size_dist_entry_t easy_dist[] = { static const asteroid_size_dist_entry_t easy_dist[] = {
@@ -85,6 +99,9 @@ static bool paused;
static unsigned asteroid_count; static unsigned asteroid_count;
static uint8_t counter; static uint8_t counter;
static unsigned score; static unsigned score;
static char *msg;
static unsigned armour_level;
static bool item;
static void cleared() static void cleared()
{ {
@@ -101,6 +118,17 @@ static void die()
counter = 0; counter = 0;
} }
static void upgrade_armour()
{
if (armour_level >= 9)
return;
static char armour_msg[] = "ARMOUR X";
armour_msg[7] = '0' + ++armour_level;
msg = armour_msg;
counter = 0;
}
static void shot_collide(unsigned a, unsigned b, physics_sep_t sep) static void shot_collide(unsigned a, unsigned b, physics_sep_t sep)
{ {
entity_mark(a); entity_mark(a);
@@ -112,13 +140,30 @@ static void shot_collide(unsigned a, unsigned b, physics_sep_t sep)
} }
} }
static void ship_collide(unsigned, unsigned, physics_sep_t) static void ship_collide(unsigned a, unsigned b, physics_sep_t sep)
{ {
const float v = sep.va - sep.vb;
const float impact = physics_get(b)->mass * v;
const float armour = armour_level * ARMOUR_COEFF;
if (impact < armour)
physics_bounce(a, b, sep);
else
die(); die();
} }
static void item_collide(unsigned a, unsigned b, physics_sep_t)
{
item = false;
entity_mark(a);
if (b == ship_entity_id)
upgrade_armour();
}
static void shoot() static void shoot()
{ {
if (dead || paused)
return;
physics_t *ship = physics_get(ship_entity_id); physics_t *ship = physics_get(ship_entity_id);
const vec2_t pos const vec2_t pos
@@ -162,10 +207,12 @@ static void anim_fire(vec2_t *verts)
static void create_field() static void create_field()
{ {
item = false;
dead = false; dead = false;
clear = false; clear = false;
paused = false; paused = false;
asteroid_count = 0; asteroid_count = 0;
msg = nullptr;
renderer_set_wrap(true); renderer_set_wrap(true);
@@ -200,6 +247,7 @@ static void reset()
{ {
level = INIT_LEVEL; level = INIT_LEVEL;
score = 0; score = 0;
armour_level = 0;
asteroids_clear(); asteroids_clear();
create_field(); create_field();
} }
@@ -229,6 +277,24 @@ static void ship_update()
win(); win();
} }
static void add_item()
{
const unsigned id = entity_add();
for (unsigned i = 0; i < NELEMS(item_verts); ++i)
scene_add(id, item_verts[i], item_counts[i], false);
const vec2_t pos = {
.x = ITEM_SPAWN_R * rng_plusminus(),
.y = ITEM_SPAWN_R * rng_plusminus(),
};
physics_add(id, pos, MAT2_ID, (vec2_t) {}, 0, 0);
collisions_add(id, ITEM_COLLIDE_R, ITEM_COLLIDE_PRIOR, item_collide);
item = true;
}
void game_init() void game_init()
{ {
input_on_shoot(shoot); input_on_shoot(shoot);
@@ -241,8 +307,10 @@ void game_init()
void game_update() void game_update()
{ {
if (dead || clear || paused) if (msg != nullptr || dead || clear || paused)
++counter; ++counter;
if (counter > COUNTER_MASK)
msg = nullptr;
if (dead || paused) if (dead || paused)
return; return;
@@ -252,6 +320,11 @@ void game_update()
scene_update(); scene_update();
entities_purge(); entities_purge();
const bool items_enabled = score >= ITEM_MIN_SCORE;
if (items_enabled && !item
&& rng_canon() < 1.0f / (60 * ITEM_SPAWN_EXP_S))
add_item();
} }
void game_draw() void game_draw()
@@ -261,16 +334,20 @@ void game_draw()
scene_draw(); scene_draw();
text_draw_score(score); text_draw_score(score);
if (clear)
if (paused && !(counter & COUNTER_MASK)) { draw_arrow();
text_draw_centre("PAUSED"); const bool display_text = !(counter & COUNTER_MASK);
return; if (display_text) {
} if (dead)
if (dead && !(counter & COUNTER_MASK))
text_draw_centre("GAME OVER"); text_draw_centre("GAME OVER");
if (clear) { if (clear) {
draw_arrow(); draw_arrow();
if (!(counter & COUNTER_MASK)) if (!(counter & COUNTER_MASK))
text_draw_centre("CLEAR"); text_draw_centre("CLEAR");
} }
if (paused)
text_draw_centre("PAUSED");
if (msg != nullptr)
text_draw_centre(msg);
}
} }