184 lines
4.4 KiB
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;
|
|
}
|