BosonX/src/generator/gen1.cpp

250 lines
6.7 KiB
C++

#include "../generator.h"
#include "../level.h"
#include "../util.h"
/* Geometric parameters */
#define G_PLATFORM_LEN_INITIAL num(6.0)
#define G_UNIT num(1.0) /* Platform length unit */
/* Probabilities */
#define P_ANY_BLUE num(0.15)
#define P_FUNNEL_BLUE num(0.5)
#define P_FUNNEL_ALLBLUE num(0.4)
#define P_RANDOM_TUNNEL_HOLE num(0.25)
GeonGenerator::GeonGenerator(): m_multi_carver{2}
{
srand(0xc0ffee);
m_initial = true;
m_z = 0.0;
m_phase = 0;
int faces[2] = { 0, 2 };
m_multi_carver.set_faces(faces);
m_multi_carver.set_noskip(true);
m_multi_carver.set_checkerboard(false);
}
/* Generates a tunnel block, which is full ring alternating with half rings.
The total length (which should be odd) is specified as a parameter.
0123456789
########## [h=0, 2u]
#.#.#.#.#. [h=0, 3u]
########## [h=0, 2u]
(...repeats)
There is a random probability for any of the platforms to turn into a 1.5u
blue followed by a whatever's left as white. */
num GeonGenerator::generate_tunnel_block(struct level *level, num z, int sec)
{
for(int i = 0; i < sec; i++) {
num len = (i & 1) ? 3 * G_UNIT : 2 * G_UNIT;
/* Generate all/even platforms on even/odd i */
for(int j = 0; j < PLATFORM_COUNT; j++) {
if((i & j) & 1)
continue;
struct platform p;
p.type = PLATFORM_WHITE;
p.face = j;
p.z = z;
p.length = len;
p.height = 0;
if(num_rand() <= P_ANY_BLUE) {
p.length = G_UNIT;
p.type = PLATFORM_BLUE;
add(level, p);
p.z += p.length;
p.length = len - p.length;
p.type = PLATFORM_WHITE;
add(level, p);
}
else {
add(level, p);
}
}
z += len;
}
return z;
}
/* Generates a blue transition, a single-block long ring with half the faces
filled with blue platforms.
0123456789
#.#.#.#.#. [h=0, 3u, all blue] */
num GeonGenerator::generate_blue_transition(struct level *level, num z)
{
num len = 3 * G_UNIT;
for(int j = 0; j < PLATFORM_COUNT / 2; j++) {
struct platform p;
p.type = PLATFORM_BLUE;
p.face = 2 * j;
p.z = z;
p.length = len;
p.height = 0;
add(level, p);
}
return z + len;
}
/* Generates a blue funnel leading to face #3-#5.
0123456789
#.#.#.#.#. [h=0, 3u]
.#.#.#.#.. [h=0, 3u]
..#.#.#... [h=1, 3u] */
num GeonGenerator::generate_blue_funnel(struct level *level, num z)
{
num len = 3 * G_UNIT;
bool allblue = (num_rand() <= P_FUNNEL_ALLBLUE);
for(int i = 0; i < 3; i++) {
/* Generate 5-i platforms starting at i, spaced by 2 */
for(int j = 0; j < 5-i; j++) {
struct platform p;
p.type = PLATFORM_BLUE;
p.face = i + 2*j;
p.z = z;
p.length = len;
p.height = (i == 2 ? 1 : 0) * PLATFORM_HEIGHT_STEP;
if(!allblue && num_rand() > P_FUNNEL_BLUE)
p.type = PLATFORM_WHITE;
add(level, p);
}
z += len;
}
return z;
}
/* Generates a random tunnel which arbitrary blocks missing. Any remaining
block can be randomly split between a blue and a rest. */
num GeonGenerator::generate_random_tunnel(struct level *level, num z, int sec)
{
for(int i = 0; i < sec; i++) {
num len = (i & 1) ? 3 * G_UNIT : 2 * G_UNIT;
for(int j = 0; j < PLATFORM_COUNT; j++) {
if(num_rand() <= P_RANDOM_TUNNEL_HOLE)
continue;
struct platform p;
p.type = PLATFORM_WHITE;
p.face = j;
p.z = z;
p.length = len;
p.height = 0;
if(num_rand() <= P_ANY_BLUE) {
p.length = G_UNIT;
p.type = PLATFORM_BLUE;
add(level, p);
p.z += p.length;
p.length = len - p.length;
p.type = PLATFORM_WHITE;
add(level, p);
}
else {
add(level, p);
}
}
z += len;
}
return z;
}
void GeonGenerator::change_phase(void)
{
static int const freq_table[] = {
0, 0, 0, /* Tunnel block */
1, 1, /* Blue transition */
2, /* Blue funnel */
3, 3, 3, /* Random tunnel */
4, 4, 4, /* Path carver */
};
int const FREQ_TABLE_SIZE = sizeof freq_table / sizeof freq_table[0];
int p = m_phase;
while(1) {
p = freq_table[rand() % FREQ_TABLE_SIZE];
/* Do not repeat the same phase twice in a row */
if(p == m_phase)
continue;
/* Do not follow blue transition with blue funnel or vice-versa */
if((p == 1 && m_phase == 2) || (p == 2 && m_phase == 1))
continue;
/* Only allow blue transitions when after a tunnel-like structure */
if(p == 1 && (m_phase != 0 && m_phase != 3))
continue;
/* Only allow tunnel-like structures when exiting a blue transition */
if(m_phase == 1 && (p != 0 && p != 3))
continue;
break;
}
m_phase = p;
}
void GeonGenerator::generate(struct level *level)
{
/* Friendly platform at start of level */
if(m_initial) {
struct platform p;
p.type = PLATFORM_WHITE;
p.face = 0;
p.z = m_z;
p.length = G_PLATFORM_LEN_INITIAL;
p.height = 0.0;
p.falling = false;
level->platform_buffer.push_back(p);
m_z += p.length;
m_initial = false;
/* Start with a tunnel block */
m_phase = 0;
}
if(m_phase == 0) {
int sections = 1 + 2 * (rand() % 2);
m_z = this->generate_tunnel_block(level, m_z, sections);
this->change_phase();
}
else if(m_phase == 1) {
m_z = this->generate_blue_transition(level, m_z);
this->change_phase();
}
else if(m_phase == 2) {
m_z = this->generate_blue_funnel(level, m_z);
m_multi_carver.force_to_match(4);
m_phase = 4;
}
else if(m_phase == 3) {
int sections = 3 + rand() % 7;
m_z = this->generate_random_tunnel(level, m_z, sections);
this->change_phase();
}
else if(m_phase == 4) {
int length = 3 + rand() % 7;
for(int i = 0; i < length; i++) {
num platform_length = rand() % 2 ? 2 * G_UNIT : 3 * G_UNIT;
m_multi_carver.next(level, m_z, platform_length);
m_z += platform_length;
}
this->change_phase();
}
}