330 lines
9.1 KiB
C++
330 lines
9.1 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)
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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, 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 = (num_rand() <= P_TUNNEL_RED) ? PLATFORM_RED : type;
|
|
p.face = j;
|
|
p.z = z;
|
|
p.length = len;
|
|
p.height = 0;
|
|
add(level, p);
|
|
}
|
|
z += 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 == 1);
|
|
|
|
/* 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 */
|
|
m_checkerboard_carver.next(level, m_z, G_PLATFORM_LEN);
|
|
|
|
/* 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) {
|
|
// TODO
|
|
this->change_phase();
|
|
}
|
|
|
|
/* Phase #1: Almost-full tunnel with random holes? */
|
|
|
|
/* Phase #2: Path with spacing + random onlookers */
|
|
|
|
/* Phase #3: Full + odd + even tunnel combination leading into #0 */
|
|
|
|
|
|
/* Phase #5: Obstacles
|
|
- ##.##.#.#.
|
|
- .##.##.#.#
|
|
- ..#..#..#.
|
|
- .#..#..#.. */
|
|
|
|
}
|