You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
6.5 KiB
262 lines
6.5 KiB
#include "environment.h" |
|
#include "game.h" |
|
#include "types.h" |
|
|
|
|
|
|
|
struct environment * environment_watch = NULL; |
|
|
|
|
|
/* ---------------------------------------------------------------------------- |
|
* Environment / World Generation |
|
* ---------------------------------------------------------------------------- |
|
* |
|
* Worlds have to be shared across threads, since they contain both collision |
|
* bodies and drawing information. Realistically, these should be stored in |
|
* separate datastructures. My solution has been to just store a copy in each |
|
* thread, and lock everything when you move to the next level and need to |
|
* generate a new one. |
|
* |
|
* An 'environment' consists of the cave structure with the collision bodies, |
|
* as well as the colourscheme for drawing it. These are procedurally |
|
* generated. A custom PRNG function is used so the same levels are given on |
|
* every platform. |
|
* |
|
*/ |
|
|
|
const struct environment* get_scene_watch(void) { |
|
return environment_watch; |
|
} |
|
|
|
double linear_interpolate(double a0, double a1, double w) { |
|
return (1.0f - w) * a0 + w * a1; |
|
} |
|
|
|
int destroy_environment(struct environment *e) { |
|
|
|
arlst_destroy(&e->ceil); |
|
arlst_destroy(&e->floor); |
|
arlst_destroy(&e->bg1); |
|
arlst_destroy(&e->bg2); |
|
|
|
return 0; |
|
} |
|
|
|
double perlin(Vect coordinate) { |
|
Vect a = coordinate; |
|
Vect b = coordinate; |
|
a.x = (int)a.x; |
|
a.y = (int)a.y; |
|
b.x = a.x + 1; |
|
b.y = a.y + 1; |
|
|
|
double sx = coordinate.x - a.x; |
|
double sy = coordinate.y - a.y; |
|
|
|
Vect d = b; |
|
d.y = a.y; |
|
|
|
double n0 = vect_dot(a, coordinate); |
|
double n1 = vect_dot(d, coordinate); |
|
|
|
double ix0 = linear_interpolate(n0, n1, sx); |
|
|
|
Vect c; |
|
c.x = a.x; |
|
c.y = b.y; |
|
|
|
n0 = vect_dot(c, coordinate); |
|
n1 = vect_dot(b, coordinate); |
|
double ix1 = linear_interpolate(n0, n1, sy); |
|
|
|
return linear_interpolate(ix0, ix1, sy); |
|
} |
|
|
|
struct colour_pallete get_pallete_high_contrast(void) { |
|
struct colour white = {.r = 255, .g=255,.b=255, .sp=CS_RGB}; |
|
struct colour black = {.r = 0, .g=0,.b=0, .sp=CS_RGB}; |
|
struct colour_pallete p = {.bg = black, .fg1 = white, .fg2 = white, .fg3 = white}; |
|
return p; |
|
} |
|
|
|
|
|
struct colour_pallete get_pallete (int seed) { |
|
struct colour base; |
|
|
|
int n = seed; |
|
int red = get_rand(&n) % 255; |
|
int blue = get_rand(&n) % 255; |
|
int green = get_rand(&n) % 255; |
|
|
|
base.r = red; |
|
base.g = green; |
|
base.b = blue; |
|
|
|
struct colour c1 = get_hsv(base); |
|
c1 = get_hsv(base); |
|
struct colour_pallete cols; |
|
|
|
c1.l = 0.1; |
|
cols.bg = get_rgb(c1); |
|
c1.l += 0.3; |
|
|
|
c1.h += 30; |
|
cols.fg1 = get_rgb(c1); |
|
|
|
c1.h += 30; |
|
cols.fg2 = get_rgb(c1); |
|
|
|
c1.h += 30; |
|
cols.fg3 = get_rgb(c1); |
|
|
|
return cols; |
|
|
|
struct colour *adj = get_adjacent(base, 20, 4); |
|
ArrayList l = new_arlst_wfree(4, free); |
|
|
|
for (int i = 0; i < 4; i++) { |
|
arlst_add(&l, adj + i); |
|
} |
|
|
|
} |
|
|
|
int comp(const void *one, const void *two) { |
|
return *(int*)one >= *(int*)two; |
|
} |
|
|
|
void swap_palletes(struct environment *e) { |
|
struct colour_pallete b = e->colours; |
|
e->colours = e->coloursb; |
|
e->coloursb = b; |
|
} |
|
|
|
/* |
|
* |
|
* Generate or just return the environment seeded by the current level number. |
|
* |
|
*/ |
|
struct environment get_scene_at(Vect coordinate, int seed) { |
|
// TODO: Environment needs to be shared between threads |
|
// - this implementation duplicates it: each thread that calls |
|
// get_scene_at gets manages its state in its own static vairables in |
|
// this function but share pointers to the malloced environment object- |
|
// it is thoroughly broken. |
|
// - Fix by having a heap allocated environment object that the draw thread |
|
// (T0) can watch and the physics thread (T3) can modify |
|
// - the entry point can either be through a function returning |
|
// a pointer or just a global const pointer |
|
// |
|
// basic cache for the last room generated |
|
static bool init; |
|
static Vect last_room; |
|
static struct environment e; |
|
static int oldseed; |
|
Vect room_coord; |
|
room_coord.x = (int)coordinate.x - (int)coordinate.x % E_ROOM_WIDTH; |
|
room_coord.y = 0; |
|
|
|
if (init && room_coord.x == last_room.x && oldseed == seed) { |
|
return e; |
|
} else if (init) { |
|
destroy_environment(&e); |
|
} |
|
last_room = room_coord; |
|
oldseed = seed; |
|
|
|
e.floor = new_arlst_wfree(E_ROOM_TILES, free); |
|
e.ceil = new_arlst_wfree(E_ROOM_TILES, free); |
|
e.bg1 = new_arlst_wfree(E_ROOM_TILES, free); |
|
e.bg2 = new_arlst_wfree(E_ROOM_TILES, free); |
|
|
|
e.colours = get_pallete(seed); |
|
e.coloursb = get_pallete_high_contrast(); |
|
|
|
if (draw_watch.high_contrast_mode) |
|
swap_palletes(&e); |
|
|
|
Vect bit; |
|
bit.y = 100 * seed; |
|
Vect pos; |
|
|
|
int n = seed; |
|
get_rand(&n); |
|
|
|
Vect node; |
|
node.y = 0; |
|
|
|
for (int i = 0; i < E_ROOM_WIDTH; i += E_ROOM_RES) { |
|
bit.x = room_coord.x + i; |
|
bit.y = seed; |
|
node.x = bit.x; |
|
|
|
int r1 = 1.5*(get_rand(&n) % 500) - (get_rand(&n) % 100); |
|
int r2 = 1.5*(get_rand(&n) % 200) - (get_rand(&n) % 200); |
|
int r3 = (get_rand(&n) % 100) - (get_rand(&n) % 500); |
|
int r4 = 50; |
|
|
|
int r[4] = {r1, r2, r3, r4}; |
|
qsort(r, 4, sizeof(int), comp); |
|
|
|
int h[4] = {node.y,node.y,node.y,node.y}; |
|
h[0] += r[0]; |
|
h[1] = h[0] + r[1]; |
|
h[2] += h[0] + r[2]; |
|
h[3] += h[0] + r[3]; |
|
qsort(h, 4, sizeof(int), comp); |
|
|
|
Vect *z = malloc(sizeof(Vect)); |
|
*z = node; |
|
z->y = h[0]; |
|
|
|
arlst_add(&e.floor, z); |
|
|
|
z = malloc(sizeof(Vect)); |
|
*z = node; |
|
z->y = h[1]; |
|
arlst_add(&e.bg1, z); |
|
|
|
z = malloc(sizeof(Vect)); |
|
*z = node; |
|
z->y = h[2]; |
|
arlst_add(&e.bg2, z); |
|
|
|
|
|
z = malloc(sizeof(Vect)); |
|
*z = node; |
|
z->y = h[3]; |
|
arlst_add(&e.ceil, z); |
|
|
|
} |
|
|
|
Vect *v = arlst_get(&e.floor, 0); |
|
Vect v2 = *(Vect *)arlst_get(&e.ceil, 0); |
|
|
|
Vect v3 = vect_add(v2, vect_scalar(vect_add(*v, vect_scalar(v2, -1)) , 0.5)); |
|
|
|
int d[] = {0, E_ROOM_TILES - 1}; |
|
int a; |
|
for (int i = 0; i <= 1; i++) { |
|
a = d[i]; |
|
v = arlst_get(&e.floor, a); |
|
v->y = v3.x; |
|
v = arlst_get(&e.ceil, a); |
|
v->y = v3.x; |
|
v = arlst_get(&e.bg1, a); |
|
v->y = v3.x; |
|
v = arlst_get(&e.bg2, a); |
|
v->y = v3.x; |
|
} |
|
|
|
// join the end and start together |
|
|
|
if (!init) { |
|
struct environment *ee = malloc(sizeof(struct environment)); |
|
environment_watch = ee; |
|
} |
|
|
|
*environment_watch = e; |
|
|
|
init = true; |
|
|
|
return e; |
|
}
|
|
|