diff --git a/game.c b/game.c index ca2caa3..ae1db77 100644 --- a/game.c +++ b/game.c @@ -15,7 +15,8 @@ #define FIRE_JITTER 0.01 #define ASTEROID_MIN_VERTS 5 -#define ASTEROID_VERT_RANGE (MAX_VERTS - ASTEROID_MIN_VERTS) +#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 @@ -27,11 +28,22 @@ #define ASTEROID_R_JITTER 0.02 #define ASTEROID_MIN_DIST 0.8 +#define REPLACE_MIN 2 +#define REPLACE_MAX 4 +#define REPLACE_RANGE (REPLACE_MAX - REPLACE_MIN) +#define REPLACE_R_COEFF 0.5 +#define REPLACE_R_JITTER 0.02 +#define REPLACE_A_JITTER 0.4 +#define REPLACE_V_JITTER 0.001 +#define REPLACE_RADIAL_V_COEFF 0.005 +#define REPLACE_OMG_JITTER 0.01 + #define LIN_PWR 0.0001 #define ROT_PWR 0.002 #define SHOT_VEL 0.04 #define SHOT_COLLIDE_R 0.005 +#define SHOT_MASS 0.005 #define MAX_SHAPES 256U #define MAX_ENTITIES 128U @@ -69,6 +81,8 @@ typedef struct { typedef struct { unsigned entities[2]; + float vs[2]; + vec2_t normal; } collision_t; static const vec2_t ship_verts[] = { @@ -268,9 +282,19 @@ static unsigned check_collisions(collision_t *out) for (unsigned i = 0; i < entity_count; ++i) { for (unsigned j = i + 1; j < entity_count; ++j) { if (intersecting(i, j)) { + const vec2_t n + = vec2_norm(vec2_sub(entities[j].pos, entities[i].pos)); + const float vi = vec2_dot(entities[i].vel, n); + const float vj = vec2_dot(entities[j].vel, n); + if (vi < 0 && vj > 0) + continue; + assert(count < MAX_COLLISIONS); out[count].entities[0] = i; out[count].entities[1] = j; + out[count].vs[0] = vi; + out[count].vs[1] = vj; + out[count].normal = n; ++count; } } @@ -278,16 +302,18 @@ static unsigned check_collisions(collision_t *out) return count; } -static void bounce_asteroids(entity_t *a, entity_t *b) +static void bounce(collision_t c) { + entity_t *a = entities + c.entities[0]; + entity_t *b = entities + c.entities[1]; + const float ma = a->radius * a->radius; const float mb = b->radius * b->radius; const float m = ma + mb; - const vec2_t n = vec2_norm(vec2_sub(b->pos, a->pos)); - - const float va1 = vec2_dot(a->vel, n); - const float vb1 = vec2_dot(b->vel, n); + const vec2_t n = c.normal; + const float va1 = c.vs[0]; + const float vb1 = c.vs[1]; const float va2 = (va1 * (ma - mb) + 2 * mb * vb1) / m; const float vb2 = (vb1 * (mb - ma) + 2 * ma * va1) / m; @@ -296,6 +322,46 @@ static void bounce_asteroids(entity_t *a, entity_t *b) b->vel = vec2_add(b->vel, vec2_scale(n, vb2 - vb1)); } +static void destroy_asteroid(entity_t *a, vec2_t shot_vel) +{ + a->dead = true; + + float r; + if (fabs(a->radius - ASTEROID_LARGE) < 1e-3) + r = ASTEROID_MEDIUM; + else if (fabs(a->radius - ASTEROID_MEDIUM) < 1e-3) + r = ASTEROID_SMALL; + else + return; + + const unsigned n = REPLACE_MIN + rng_uint32() % REPLACE_RANGE; + const float da = 2 * PI / n; + + const float m = a->radius * a->radius; + const vec2_t p + = vec2_add(vec2_scale(a->vel, m), vec2_scale(shot_vel, SHOT_MASS)); + const vec2_t inherit_v = vec2_scale(p, 1 / n * (m + SHOT_MASS)); + + for (unsigned i = 0; i < n; ++i) { + const float disp_r = REPLACE_R_COEFF * a->radius + + REPLACE_R_JITTER * rng_plusminus(); + const float disp_a = i * da + REPLACE_A_JITTER * rng_plusminus(); + const vec2_t disp = { disp_r * cosf(disp_a), disp_r * sinf(disp_a) }; + + const vec2_t radial_v = vec2_scale( + vec2_norm(disp), REPLACE_RADIAL_V_COEFF * rng_canon()); + const vec2_t jitter_v = { + REPLACE_V_JITTER * rng_plusminus(), + REPLACE_V_JITTER * rng_plusminus(), + }; + + entity_t *e = gen_asteroid(r, nullptr); + e->pos = vec2_add(a->pos, disp); + e->vel = vec2_add(inherit_v, vec2_add(radial_v, jitter_v)); + e->omg = a->omg / n + REPLACE_OMG_JITTER * rng_plusminus(); + } +} + static void handle_collisions(const collision_t *collisions, unsigned count) { for (unsigned i = 0; i < count; ++i) { @@ -312,17 +378,17 @@ static void handle_collisions(const collision_t *collisions, unsigned count) continue; if (a->tag == COLLISION_ASTEROID && b->tag == COLLISION_ASTEROID) { - bounce_asteroids(a, b); + bounce(c); continue; } if (a->tag == COLLISION_SHOT) { a->dead = true; - b->dead = true; + destroy_asteroid(b, a->vel); } if (b->tag == COLLISION_SHOT) { - a->dead = true; b->dead = true; + destroy_asteroid(a, b->vel); } }