BosonX/src/generator/gen2.cpp

384 lines
11 KiB
C++

#include "../generator.h"
#include "../level.h"
#include "../util.h"
#include <stdlib.h>
/* Geometric parameters */
#define G_PLATFORM_LEN num(2.5)
#define G_PLATFORM_SPC num(0.5)
#define G_PLATFORM_LEN_INITIAL num(6.0)
/* Probabilities */
#define P_ASCF_RED num(0.2)
#define P_TUNNEL_RED num(0.2)
#define P_TUNNEL_RED_RING num(0.15)
#define P_TUNNEL_BLUE num(0.05)
AcceleronGenerator::AcceleronGenerator():
m_checkerboard_carver{3}
{
srand(123456);
m_initial = true;
m_z = 0.0;
m_phase = 0;
int faces[3] = { 0, 4, 8 };
m_checkerboard_carver.set_faces(faces);
m_checkerboard_carver.set_noskip(true);
m_checkerboard_carver.set_checkerboard(true);
}
/* Generates an ascending funnel up to a long red platform on face #4, in the
following shape.
0123456789
#.#.#.#.#. [h=0]
.#.#.#.#.. [h=1]
..#.#.#... [h=2]
...#.#.... [h=3]
....#..... [h=4]
....##.... [h=5]
...##..... [h=6]
....##.... [h=7]
....R..... [h=8]
....|.....
....|.....
....|#.... [h=4]
...B.|.... [h=5]
...|.|....
...|.|....
....#..... [h=1]
Returns updated z. */
num AcceleronGenerator::generate_ascending_funnel(struct level *level, num z)
{
/* Ascending funnel */
for(int i = 0; i < 5; 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_WHITE;
p.face = i + 2*j;
p.z = z;
p.length = G_PLATFORM_LEN;
p.height = i * PLATFORM_HEIGHT_STEP;
if(num_rand() <= P_ASCF_RED)
p.type = PLATFORM_RED;
add(level, p);
}
z += G_PLATFORM_LEN + G_PLATFORM_SPC;
}
/* Steps */
for(int i = 0; i < 3; i++) {
struct platform p;
p.type = PLATFORM_WHITE;
p.face = 4;
p.z = z;
p.length = G_PLATFORM_LEN;
p.height = (i + 5) * PLATFORM_HEIGHT_STEP;
add(level, p);
p.face = (i & 1) ? 3 : 5;
add(level, p);
z += G_PLATFORM_LEN + G_PLATFORM_SPC;
}
/* Overlapping red platform and blue/white neighbors */
struct platform p;
p.type = PLATFORM_RED;
p.face = 4;
p.z = z;
p.length = G_PLATFORM_LEN * 3;
p.height = 8 * PLATFORM_HEIGHT_STEP;
add(level, p);
z += G_PLATFORM_LEN * 2 + G_PLATFORM_SPC;
p.type = PLATFORM_BLUE;
p.face = 3;
p.z = z + G_PLATFORM_LEN;
p.length = G_PLATFORM_LEN * 3;
p.height = 5 * PLATFORM_HEIGHT_STEP;
add(level, p);
p.type = PLATFORM_WHITE;
p.face = 5;
p.z = z;
p.length = G_PLATFORM_LEN * 2;
p.height = 4 * PLATFORM_HEIGHT_STEP;
add(level, p);
z += G_PLATFORM_LEN * 3 + G_PLATFORM_SPC;
/* Exit platform */
p.type = PLATFORM_WHITE;
p.face = 4;
p.z = z;
p.length = G_PLATFORM_LEN;
p.height = 0.0;
add(level, p);
z += G_PLATFORM_LEN + G_PLATFORM_SPC;
return z;
}
static platform_type_t tunnel_type(void)
{
if(num_rand() <= P_TUNNEL_BLUE)
return PLATFORM_BLUE;
else if(num_rand() <= P_TUNNEL_RED)
return PLATFORM_RED;
else
return PLATFORM_WHITE;
}
/* 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]
#.#.#.#.#. [h=0]
|.|.|.|.|.
########## [h=0]
(...repeats)
There is a random probability for any of the platforms to be red or blue,
and also a random probability for entire rings to be red. */
num AcceleronGenerator::generate_tunnel_block(struct level *level, num z,
int sec)
{
for(int i = 0; i < sec; i++) {
num len = (1 + (i & 1)) * G_PLATFORM_LEN;
platform_type_t type = PLATFORM_WHITE;
if(num_rand() <= P_TUNNEL_RED_RING)
type = PLATFORM_RED;
/* 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 = (type == PLATFORM_WHITE) ? tunnel_type() : type;
p.face = j;
p.z = z;
p.length = len;
p.height = 0;
add(level, p);
}
z += len;
}
return z;
}
/* Generates a fractured tunnel, which is a full ring followed by random-length
platforms varying between 0 and 3 units. This is repeated for as many
sections as requested.
0123456789
########## [h=0]
##..###.## [h=0]
||#.||.#|.
.|..|..|..
(...repeats)
There is a random probability for any of the platforms to be red or blue. */
num AcceleronGenerator::generate_fractured_tunnel(struct level *level, num z,
int sec)
{
for(int i = 0; i < sec; i++) {
/* Initial ring */
for(int j = 0; j < PLATFORM_COUNT; j++) {
struct platform p;
p.type = tunnel_type();
p.face = j;
p.z = z;
p.length = G_PLATFORM_LEN;
p.height = 0;
add(level, p);
}
/* Fractured ring */
for(int j = 0; j < PLATFORM_COUNT; j++) {
int start = (num_rand() <= num(0.3)) ? 1 : 0;
int length = rand() % (4 - start);
if(!length)
continue;
struct platform p;
p.type = tunnel_type();
p.face = j;
p.z = z + (start + 1) * G_PLATFORM_LEN;
p.length = length * G_PLATFORM_LEN;
p.height = 0;
add(level, p);
}
z += 4 * G_PLATFORM_LEN;
}
return z;
}
void AcceleronGenerator::change_phase(void)
{
static int const freq_table[] = {
0, /* Ascending funnel */
1, /* Random paths */
2, /* Tunnel block */
3, /* Random path with walls */
4, /* Incomplete tunnel block */
};
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;
/* Only allow ascending funnel after tunnel blocks */
if(p == 0 && (m_phase != 2 && m_phase != 4))
continue;
/* Do not follow tunnel blocks together */
if((p == 2 && m_phase == 4) || (p == 4 && m_phase == 2))
continue;
break;
}
m_phase = p;
}
void AcceleronGenerator::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;
add(level, p);
m_z += p.length;
m_initial = false;
m_phase = 3;
}
/* Phase #0: Ascending funnel and long/tight red platform */
if(m_phase == 0) {
m_z = this->generate_ascending_funnel(level, m_z);
m_checkerboard_carver.force_to_match(4);
this->change_phase();
}
/* Phase #1: Checkerboard random paths */
else if(m_phase == 1) {
int length = 4 + 2 * (rand() % 2);
for(int i = 0; i < length; i++) {
m_checkerboard_carver.next(level, m_z, G_PLATFORM_LEN);
m_z += G_PLATFORM_LEN + G_PLATFORM_SPC;
}
this->change_phase();
}
/* Phase #2: Simple tunnel block */
else if(m_phase == 2) {
int length = 5 + 2 * (rand() % 2);
m_z = this->generate_tunnel_block(level, m_z, length);
this->change_phase();
}
/* Phase #3: Checkerboard random paths, with dividing walls */
else if(m_phase == 3) {
int length = 14;
bool occupied[PLATFORM_COUNT];
int faces[3];
for(int i = 0; i < length; i++) {
/* Add walls at i=2,5,8,11 on any face where there is no platform
at iteration i or iteration i+1. This is to prevent the
following situation:
W#W
#..
where you can't side-jump from the middle platform to the left
one because the wall prevents it. */
bool walls = (i == 2 || i == 5 || i == 8 || i == 11);
/* Start by excluding platforms from iteration #i */
if(walls) {
for(int j = 0; j < PLATFORM_COUNT; j++)
occupied[j] = false;
m_checkerboard_carver.get_faces(faces);
for(int j = 0; j < 3; j++)
occupied[faces[j]] = true;
}
/* Advance */
int height = (i == 13) ? 2 : (i == 12) ? 1 : (i / 3);
m_checkerboard_carver.next(level, m_z, G_PLATFORM_LEN,
height * PLATFORM_HEIGHT_STEP);
/* Generate blocks and walls */
if(walls) {
/* Exclude platforms from iteration #i+1 */
m_checkerboard_carver.get_faces(faces);
for(int j = 0; j < 3; j++)
occupied[faces[j]] = true;
for(int j = 0; j < PLATFORM_COUNT; j++) {
/* Generate blocks in empty spaces */
if(!occupied[j]) {
struct platform p;
p.type = PLATFORM_BLOCK;
p.face = j;
p.z = m_z;
p.length = G_PLATFORM_LEN;
p.height = 0.0;
add(level, p);
p.type = PLATFORM_BLOCK_TOP;
add(level, p);
}
/* Generate dividing walls on edges */
if(occupied[j] != occupied[(j+1) % PLATFORM_COUNT]) {
struct platform p;
p.type = PLATFORM_SEPARATING_WALL;
p.face = (j+1) % PLATFORM_COUNT;
p.z = m_z;
p.length = G_PLATFORM_LEN;
p.height = 0.0;
p.wall_left_sided = !occupied[j];
p.wall_right_sided = occupied[j];
add(level, p);
}
}
}
m_z += G_PLATFORM_LEN + G_PLATFORM_SPC;
}
this->change_phase();
}
/* Phase #4: Incomplete tunnel block */
else if(m_phase == 4) {
m_z = this->generate_fractured_tunnel(level, m_z, 3);
this->change_phase();
}
/* Phase #5: Fixed obstacles
- ##.##.#.#.
- .##.##.#.#
- ..#..#..#.
- .#..#..#.. */
}