#include "asteroids.h" #include "collisions.h" #include "entity.h" #include "renderer.h" #include "rng.h" #include "scene.h" #include #include #include #define MIN_VERTS 5 #define VERT_RANGE (MAX_VERTS - MIN_VERTS) #define A_JITTER 0.8 #define V_JITTER 0.001 #define ROT_JITTER 0.005 #define R_JITTER 0.02 #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.05f, [ASTEROID_MEDIUM] = 0.1f, [ASTEROID_LARGE] = 0.2f, [ASTEROID_HUGE] = 0.4f, }; 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; }