Files
asteroids/asteroids.c

184 lines
4.4 KiB
C

#include "asteroids.h"
#include "collisions.h"
#include "entity.h"
#include "renderer.h"
#include "rng.h"
#include "scene.h"
#include <assert.h>
#include <math.h>
#include <string.h>
#define MIN_VERTS 5
#define VERT_RANGE (MAX_VERTS - MIN_VERTS)
#define A_JITTER 0.8
#define V_JITTER 0.0005
#define ROT_JITTER 0.005
#define R_JITTER 0.01
#define MIN_DIST 0.8
#define REPLACE_MIN 3
#define REPLACE_MAX 5
#define REPLACE_RANGE (REPLACE_MAX - REPLACE_MIN)
#define REPLACE_R_COEFF 0.8
#define REPLACE_R_JITTER 0.02
#define REPLACE_A_JITTER 0.4
#define REPLACE_V_JITTER 0.0005
#define REPLACE_RADIAL_V_COEFF 0.005
#define REPLACE_ROT_JITTER 0.02
#define COLLIDE_PRIOR 0
typedef struct {
bool valid;
unsigned component_id;
asteroid_size_t size;
} entry_t;
static asteroids_cb_t clear;
static unsigned count;
static entry_t entries[MAX_ENTITIES];
static float radii[] = {
[ASTEROID_SMALL] = 0.025f,
[ASTEROID_MEDIUM] = 0.05f,
[ASTEROID_LARGE] = 0.1f,
[ASTEROID_HUGE] = 0.2f,
};
static void update(unsigned new_entity_id, void *ref)
{
entry_t *old_e = (entry_t *)ref;
entry_t *new_e = entries + new_entity_id;
memcpy(new_e, old_e, sizeof(entry_t));
old_e->valid = false;
entity_update_component(new_entity_id, new_e->component_id, new_e);
}
static void remove(void *ref)
{
entry_t *e = (entry_t *)ref;
e->valid = false;
--count;
if (count == 0)
clear();
}
static void
create_asteroid(vec2_t pos, vec2_t vel, float rot, asteroid_size_t size)
{
const float r_mean = radii[size];
const unsigned n = MIN_VERTS + rng_uint32() % VERT_RANGE;
const float da = 2.0f * PI / n;
vec2_t verts[n];
for (unsigned i = 0; i < n; ++i) {
const float r = r_mean + rng_plusminus() * R_JITTER;
const float a_jitter = A_JITTER * rng_plusminus() * da / 2;
const float a = i * da + a_jitter;
verts[i] = (vec2_t) { r * cosf(a), r * sinf(a) };
}
const unsigned id = entity_add();
scene_add(id, verts, n, true);
physics_add(id, pos, MAT2_ID, vel, rot, r_mean * r_mean);
collisions_add(id, r_mean, COLLIDE_PRIOR, physics_bounce);
entry_t *entry = entries + id;
entry->valid = true;
entry->component_id = entity_add_component(id, update, remove, entry);
entry->size = size;
++count;
}
static asteroid_size_t draw_size(const asteroid_size_dist_entry_t *dist)
{
const float r = rng_canon();
float acc = 0;
for (unsigned i = 0; dist[i].probability != 0.0f; ++i) {
acc += dist[i].probability;
if (r < acc)
return dist[i].size;
}
assert(false);
}
void asteroids_clear()
{
count = 0;
memset(entries, 0, sizeof(entries));
}
void asteroids_on_clear(asteroids_cb_t cb)
{
clear = cb;
}
void asteroids_add(const asteroid_size_dist_entry_t *dist)
{
vec2_t pos;
do {
pos.y = rng_plusminus();
pos.x = aspect * rng_plusminus();
} while (vec2_len(pos) < MIN_DIST);
const vec2_t vel = {
V_JITTER * rng_plusminus(),
V_JITTER * rng_plusminus(),
};
const float rot = ROT_JITTER * rng_plusminus();
const asteroid_size_t size = draw_size(dist);
create_asteroid(pos, vel, rot, size);
}
void asteroids_hit(unsigned shot, unsigned asteroid, physics_sep_t)
{
assert(entries[asteroid].valid);
entity_mark(asteroid);
const asteroid_size_t old_size = entries[asteroid].size;
if (old_size == ASTEROID_SMALL)
return;
const asteroid_size_t size = old_size - 1;
const float mass = radii[size] * radii[size];
const physics_t *phys_shot = physics_get(shot);
const physics_t *phys_ast = physics_get(asteroid);
const vec2_t p_shot = vec2_scale(phys_shot->vel, phys_shot->mass);
const vec2_t p_ast = vec2_scale(phys_ast->vel, phys_ast->mass);
const unsigned n = REPLACE_MIN + rng_uint32() % REPLACE_RANGE;
const float da = 2 * PI / n;
const vec2_t p = vec2_add(p_shot, p_ast);
const vec2_t inherit_v = vec2_scale(p, 1 / (n * mass));
for (unsigned i = 0; i < n; ++i) {
const float r = REPLACE_R_COEFF * radii[old_size]
+ REPLACE_R_JITTER * rng_plusminus();
const float a = i * da + REPLACE_A_JITTER * rng_plusminus();
const vec2_t disp = { r * cosf(a), r * sinf(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(),
};
const vec2_t pos = vec2_add(phys_ast->pos, disp);
const vec2_t vel = vec2_add(inherit_v, vec2_add(radial_v, jitter_v));
const float rot
= phys_ast->rot / n + REPLACE_ROT_JITTER * rng_plusminus();
create_asteroid(pos, vel, rot, size);
}
}
bool asteroids_check(unsigned id)
{
return entries[id].valid;
}