1
1
Fork 0
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

#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;
}