Compare commits

...

3 Commits

Author SHA1 Message Date
Lephenixnoir 83a508598d
proper collisions for block platforms 2023-08-03 08:29:00 +02:00
Lephenixnoir 87a6d28957
level 3 basics 2023-08-03 08:21:20 +02:00
Lephenixnoir 357168205c
suggested finished level 2 generation 2023-08-03 00:12:02 +02:00
9 changed files with 281 additions and 37 deletions

View File

@ -176,12 +176,24 @@ void game_advance(struct game *game)
}
}
static bool standable(struct platform const &p)
{
return p.type != PLATFORM_SEPARATING_WALL
&& p.type != PLATFORM_BLOCK
&& p.type != PLATFORM_BLOCK_TOP;
}
static bool collidable(struct platform const &p)
{
return p.type == PLATFORM_BLOCK_TOP;
}
num game_height_at(struct game *game, num z, int face)
{
num max_h = -KILL_PLANE_RADIUS;
for(auto const &p: game->level.platform_buffer) {
if(p.face == face && z >= p.z && z <= p.z + p.length)
if(standable(p) && p.face == face && z >= p.z && z <= p.z + p.length)
max_h = (max_h >= p.height) ? max_h : p.height;
}
@ -195,7 +207,20 @@ struct platform *game_platform_under_player(struct game *game,
return NULL;
for(auto &p: game->level.platform_buffer) {
if(player->platform == p.face
if(standable(p) && player->platform == p.face
&& player->z >= p.z && player->z <= p.z + p.length) {
return &p;
}
}
return NULL;
}
struct platform *game_block_hitting_player(struct game *game,
struct player const *player)
{
for(auto &p: game->level.platform_buffer) {
if(collidable(p) && player->platform == p.face
&& player->z >= p.z && player->z <= p.z + p.length) {
return &p;
}

View File

@ -134,4 +134,8 @@ num game_height_at(struct game *game, num z, int face);
struct platform *game_platform_under_player(struct game *game,
struct player const *player);
/* Returns the block platform that the player is hitting, if any. */
struct platform *game_block_hitting_player(struct game *game,
struct player const *player);
#endif /* __GAME_H__ */

View File

@ -57,7 +57,7 @@ struct MultiPathCarver
void force_to_match(int face);
/* Add the next set of platforms (all at z) to the level. */
void next(struct level *level, num z, num length);
void next(struct level *level, num z, num length, num height=0.0);
private:
struct Path {
@ -134,6 +134,7 @@ struct AcceleronGenerator: public Generator
num generate_ascending_funnel(struct level *level, num z);
num generate_tunnel_block(struct level *level, num z, int sections);
num generate_fractured_tunnel(struct level *level, num z, int sections);
void change_phase(void);
@ -143,13 +144,21 @@ struct AcceleronGenerator: public Generator
MultiPathCarver m_checkerboard_carver;
};
struct gen3: public Generator
struct RadionGenerator: public Generator
{
gen3();
RadionGenerator();
void generate(struct level *) override;
~gen3() override = default;
~RadionGenerator() override = default;
path_carver m_path_carvers[3];
num generate_blue_spiral(struct level *level, num z);
num generate_ascending_grid(struct level *level, num z);
void change_phase(void);
num m_z;
bool m_initial;
int m_phase;
MultiPathCarver m_multi_carver;
};
#endif /* GENERATOR_H */

View File

@ -11,6 +11,7 @@
#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}
@ -123,6 +124,16 @@ num AcceleronGenerator::generate_ascending_funnel(struct level *level, num z)
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.
@ -133,8 +144,8 @@ num AcceleronGenerator::generate_ascending_funnel(struct level *level, num z)
########## [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. */
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)
@ -151,7 +162,7 @@ num AcceleronGenerator::generate_tunnel_block(struct level *level, num z,
continue;
struct platform p;
p.type = (num_rand() <= P_TUNNEL_RED) ? PLATFORM_RED : type;
p.type = (type == PLATFORM_WHITE) ? tunnel_type() : type;
p.face = j;
p.z = z;
p.length = len;
@ -164,6 +175,55 @@ num AcceleronGenerator::generate_tunnel_block(struct level *level, num z,
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[] = {
@ -253,7 +313,7 @@ void AcceleronGenerator::generate(struct level *level)
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);
bool walls = (i == 2 || i == 5 || i == 8 || i == 11);
/* Start by excluding platforms from iteration #i */
if(walls) {
@ -265,7 +325,9 @@ void AcceleronGenerator::generate(struct level *level)
}
/* Advance */
m_checkerboard_carver.next(level, m_z, G_PLATFORM_LEN);
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) {
@ -309,21 +371,13 @@ void AcceleronGenerator::generate(struct level *level)
/* Phase #4: Incomplete tunnel block */
else if(m_phase == 4) {
// TODO
m_z = this->generate_fractured_tunnel(level, m_z, 3);
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
/* Phase #5: Fixed obstacles
- ##.##.#.#.
- .##.##.#.#
- ..#..#..#.
- .#..#..#.. */
}

View File

@ -2,19 +2,166 @@
#include "../level.h"
#include <stdlib.h>
#define N PLATFORM_COUNT
/* Geometric parameters */
#define G_PLATFORM_LEN num(1.5)
#define G_PLATFORM_SPC num(0.5)
#define G_PLATFORM_LEN_INITIAL num(6.0)
gen3::gen3(): m_path_carvers{{0}, {N/3}, {2*N/3}}
RadionGenerator::RadionGenerator():
m_multi_carver{3}
{
srand(123456);
m_initial = true;
m_z = 0.0;
m_phase = 0;
int faces[3] = { 0, 4, 8 };
m_multi_carver.set_faces(faces);
m_multi_carver.set_noskip(false);
m_multi_carver.set_checkerboard(false);
}
void gen3::generate(struct level *level)
/* Generate a blue/white spiral going full circle.
0123456789
.........B
........B|
.......B|.
......B|.#
.....B|.#|
....B|.#|.
...B|.#|..
..B|.#|...
.B|.#|....
B|.#|.....
|.#|.....#
.#|......|
#|........
|......... */
num RadionGenerator::generate_blue_spiral(struct level *level, num z)
{
struct platform p;
for(int i = 0; i < 3; i++) {
if(m_path_carvers[i].next(3.0, &p)) {
level->platform_buffer.push_back(p);
for(int i = 0; i < 14; i++) {
if(i < 10) {
struct platform p;
p.type = PLATFORM_BLUE;
p.face = PLATFORM_COUNT - 1 - i;
p.z = z;
p.length = 2 * G_PLATFORM_LEN;
p.height = 0.0;
add(level, p);
}
if(i >= 3 && i <= 12) {
struct platform p;
p.type = PLATFORM_WHITE;
p.face = PLATFORM_COUNT - 1 - (i - 3);
p.z = z;
p.length = 2 * G_PLATFORM_LEN;
p.height = 0.0;
add(level, p);
}
if(i == 10) {
struct platform p;
p.type = PLATFORM_WHITE;
p.face = PLATFORM_COUNT - 1;
p.z = z;
p.length = 2 * G_PLATFORM_LEN;
p.height = 0.0;
add(level, p);
}
z += G_PLATFORM_LEN + G_PLATFORM_SPC;
}
return z;
}
/* Generate an ascending grid leading up to step #4.
0123456789
#.#.#.#.#. [h=0]
.#.#.#.#.# [h=1]
#.#.#.#.#. [h=2]
.#.#.#.#.# [h=3]
#.#.#.#.#. [h=4]
.#.#.#.#.# [h=4]
#.#.#.#.#. [h=4]
BBBBBBBBBB [h=4]
|||||||||| [h=4]
.#.#.#.#.# [h=4]
#.#.#.#.#. [h=1] */
num RadionGenerator::generate_ascending_grid(struct level *level, num z)
{
for(int i = 0; i < 11; i++) {
int height = (i <= 4) ? i : (i == 10) ? 1 : 4;
for(int j = 0; j < 10; j++) {
if((i != 7) && ((i & 1) != (j & 1)))
continue;
struct platform p;
p.type = (i == 7) ? PLATFORM_BLUE : PLATFORM_WHITE;
p.face = j;
p.z = z;
p.length = (i == 7 ? 4 : 2) * G_PLATFORM_LEN;
p.height = height * PLATFORM_HEIGHT_STEP;
add(level, p);
}
z += 2 * G_PLATFORM_LEN;
}
return z;
}
void RadionGenerator::change_phase(void)
{
static int const freq_table[] = {
0,
1,
};
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;
break;
}
m_phase = p;
}
void RadionGenerator::generate(struct level *level)
{
/* m_multi_carver.next(level, m_z, G_PLATFORM_LEN);
m_z += G_PLATFORM_LEN;
*/
/* 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 = 0;
}
if(m_phase == 0) {
m_z = this->generate_blue_spiral(level, m_z);
this->change_phase();
}
else if(m_phase == 1) {
m_z = this->generate_ascending_grid(level, m_z);
this->change_phase();
}
}

View File

@ -89,7 +89,7 @@ void MultiPathCarver::force_to_match(int face)
}
}
void MultiPathCarver::next(struct level *level, num z, num length)
void MultiPathCarver::next(struct level *level, num z, num length, num height)
{
/* Decide whether to skip */
for(int i = 0; i < m_N; i++) {
@ -108,7 +108,7 @@ void MultiPathCarver::next(struct level *level, num z, num length)
p.face = face;
p.z = z;
p.length = length;
p.height = 0;
p.height = height;
p.type = PLATFORM_WHITE;
level->platform_buffer.push_back(p);
}

View File

@ -21,8 +21,8 @@ struct level level_create(int level)
break;
case 3:
l.name = "radion";
l.gen = std::make_unique<struct gen3>();
l.bgcolor = C_WHITE;
l.gen = std::make_unique<RadionGenerator>();
l.bgcolor = RGB24(0xe17c6a);
break;
}
return l;

View File

@ -155,8 +155,10 @@ int play_level(int level_id)
level_update(&game.level, player->z, player->platform);
camera_track(camera, player);
struct platform *standing_on = game_platform_under_player(&game,
&game.player);
struct platform *standing_on =
game_platform_under_player(&game, &game.player);
struct platform *hitting =
game_block_hitting_player(&game, &game.player);
//======= Rendering =======//
@ -297,7 +299,8 @@ int play_level(int level_id)
/* Death condition #1/2: inside a platform (hit it in front) */
bool death_1 = (player->height >= floor - PLAYER_HEIGHT &&
player->height < floor - STEP_HEIGHT);
player->height < floor - STEP_HEIGHT)
|| (hitting != NULL);
/* Death condition #2/2: below the kill plane */
bool death_2 = (player->height < LEVEL_RADIUS - KILL_PLANE_RADIUS);

View File

@ -153,8 +153,10 @@ uint16_t render_platform_color(struct platform const *p,
return camera_platform_color(camera, p->face);
if(p->type == PLATFORM_BLUE)
return render_color_blue_platform(t);
if(p->type == PLATFORM_SEPARATING_WALL || p->type == PLATFORM_BLOCK_TOP)
if(p->type == PLATFORM_SEPARATING_WALL)
return RGB24(0x909090);
if(p->type == PLATFORM_BLOCK_TOP)
return RGB24(0xa0a0a0);
if(p->type == PLATFORM_BLOCK)
return RGB24(0x808080);
if(p->type == PLATFORM_RED) {