switch to arbitary-legnth platforms

This commit is contained in:
Lephenixnoir 2023-05-24 13:08:59 +02:00
parent cb76c5137c
commit 68eecf7d26
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
12 changed files with 198 additions and 211 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Build files
/build-fx
/build-cg
/build-cg-push
/*.g1a
/*.g3a

View File

@ -72,10 +72,7 @@ void player::get_anim(struct anim **anim, int *frame)
}
}
//======= Game implementation =======//
//======= General utilities =======//
//======= Functions on the game world space =======//
static vec2 platform_rotations[PLATFORM_COUNT];
static num platform_distance;
@ -87,7 +84,7 @@ num space_platform_distance(void)
/* We can't use a constructor function because g++ already generates a call to
the default constructor, which would run after us and override the data. */
void game_init(void)
void space_init(void)
{
num arc = space_platform_arc();
@ -99,18 +96,44 @@ void game_init(void)
platform_distance = LEVEL_RADIUS * num_cos(arc / 2);
}
struct prect space_platform_position(int platform_id, num z)
struct prect space_platform_position(int face, num z, num length)
{
struct prect r;
vec3 radius(0, -LEVEL_RADIUS, z);
int angle_l = platform_id;
int angle_r = (platform_id + 1) % PLATFORM_COUNT;
int angle_l = face;
int angle_r = (face + 1) % PLATFORM_COUNT;
r.nl = r.fl = vec_rotate_around_z(radius, platform_rotations[angle_l]);
r.nr = r.fr = vec_rotate_around_z(radius, platform_rotations[angle_r]);
r.fl.z += SECTION_LENGTH;
r.fr.z += SECTION_LENGTH;
r.fl.z += length;
r.fr.z += length;
return r;
}
//======= Game implementation =======//
void game_advance(struct game *game)
{
struct level *level = &game->level;
auto it = level->platform_buffer.begin();
while(it != level->platform_buffer.end()) {
/* If a platform is dead, remove it. We include the camera's distance
in the calculation because we only remove platforms that have been
passed *and* are now invisible. */
if(game->player.z - RENDER_CAMERA_BACK_DISTANCE > it->z + it->length)
it = level->platform_buffer.erase(it);
else
++it;
}
}
bool game_player_above_platform(struct game *game, num height)
{
struct player const *player = &game->player;
struct level const *level = &game->level;
}

View File

@ -73,7 +73,7 @@ struct player
/* Dynamic information about one or multiple attempts at a single level. */
struct game
{
level_t level;
struct level level;
struct player player;
struct camera camera;
@ -87,9 +87,10 @@ struct game
} debug;
};
//======= Functions on the game world space =======//
/* Initialize constants for the game. */
void game_init(void);
void space_init(void);
/* Angle, in radians, of a platform arc (2π / PLATFORM_COUNT). */
consteval num space_platform_arc()
@ -100,8 +101,17 @@ consteval num space_platform_arc()
/* Distance between the center of the world and a platform's plane. */
num space_platform_distance(void);
/* Position, in world units, of a flat platform in the specified platform slot
/* Position, in world units, of a flat platform in the specified platform face
(0..PLATFORM_COUNT-1) that has its near side at the specified depth z. */
struct prect space_platform_position(int platform_slot, num z);
struct prect space_platform_position(int face, num z, num length);
//======= Game management =======//
/* Advance in the level by discarding platforms that have been passed. */
void game_advance(struct game *game);
/* Check if the player would be standing over a platform with the provided
height coordinate. This ignores player's height value. */
bool game_player_above_platform(struct game *game, num height);
#endif /* __GAME_H__ */

View File

@ -1,52 +1,63 @@
#ifndef GENERATOR
# define GENERATOR
#ifndef GENERATOR_H
#define GENERATOR_H
struct level;
typedef struct level level_t;
#include <num/num.h>
#include "settings.h"
using namespace libnum;
/* A utility structure holding data for path carver - a generator tool that
produces a random traversable path. */
struct path
struct path_carver
{
path(int initial_pos);
void next(struct section *section);
/* Start a path on the provided face. */
path_carver(int initial_face);
/* Advance by one platform of the specified length and generate the
associated platform data. A platform might not be generated if the path
decides to skip for a jump! Returns true if a platform was generated in
*p, false otherwise (in that case don't use *p). */
bool next(num length, struct platform *p);
private:
int pos;
bool last_skipped;
/* Face where the next platform will be generated */
int m_face;
/* Current z position */
num m_z;
/* Whether last face was skipped */
bool m_last_skipped;
};
struct generator
{
virtual void generate(level_t *) = 0;
virtual void generate(struct level *) = 0;
virtual ~generator() = default;
};
struct gen1 : public generator
{
gen1();
void generate(level_t *) override;
void generate(struct level *) override;
~gen1() override = default;
path paths[3];
num m_last_z;
path_carver m_path_carvers[3];
};
struct gen2 : public generator
{
gen2();
void generate(level_t *) override;
void generate(struct level *) override;
~gen2() override = default;
int last_pos;
path_carver m_path_carver;
};
struct gen3 : public generator
{
gen3();
void generate(level_t *) override;
void generate(struct level *) override;
~gen3() override = default;
int last_pos[3];
path_carver m_path_carvers[3];
};
#endif /* GENERATOR */
#endif /* GENERATOR_H */

View File

@ -3,24 +3,26 @@
#define N PLATFORM_COUNT
gen1::gen1(): paths{{0}, {N/3}, {N*2/3}}
gen1::gen1(): m_path_carvers{{0}, {N/2}, {2*N/3}}
{
m_last_z = 0;
srand(0xc0ffee);
}
void gen1::generate(level_t *level)
void gen1::generate(struct level *level)
{
int kind = rand() % 2;
int length = 2 + rand() % 6;
for(int k = 0; k < length; k++) {
struct section section;
for(int i = 0; i < N; i++)
section.platforms[i].type = PLATFORM_EMPTY;
/* Generate an old-style "section" */
/* Kind #0: Only the random paths */
for(int i = 0; i < 3; i++)
this->paths[i].next(&section);
for(int i = 0; i < 3; i++) {
struct platform p;
if(m_path_carvers[i].next(SECTION_LENGTH, &p))
level->platform_buffer.push_back(p);
}
/* Kind #1: Also add some fixed blocks */
if(kind == 1) {
@ -28,16 +30,29 @@ void gen1::generate(level_t *level)
if(r == 0 || r == 1) {
for(int i = 0; i < N; i++) {
auto t = ((i ^ k) & 1) ? PLATFORM_WHITE : PLATFORM_EMPTY;
section.platforms[i].type = t;
if((i ^ k) & 1) {
struct platform p;
p.face = i;
p.type = PLATFORM_WHITE;
p.z = m_last_z;
p.length = SECTION_LENGTH;
level->platform_buffer.push_back(p);
}
}
}
if(r == 2) {
for(int i = 0; i < N; i++)
section.platforms[i].type = PLATFORM_WHITE;
for(int i = 0; i < N; i++) {
struct platform p;
p.face = i;
p.type = PLATFORM_WHITE;
p.z = m_last_z;
p.length = SECTION_LENGTH;
if(level)
level->platform_buffer.push_back(p);
}
}
}
level->section_buffer.push_back(section);
m_last_z += SECTION_LENGTH;
}
}

View File

@ -2,27 +2,15 @@
#include "../level.h"
#include <stdlib.h>
#define N PLATFORM_COUNT
gen2::gen2() : last_pos{0} { srand(123456); }
void gen2::generate(level_t *level)
gen2::gen2(): m_path_carver{0}
{
struct section section;
this->last_pos += (rand() % 3) - 1;
if (this->last_pos < 0)
this->last_pos = N - 1;
if (this->last_pos >= N)
this->last_pos = 0;
for (int i = 0; i < N; ++i) {
if (i == this->last_pos)
section.platforms[i].type = PLATFORM_WHITE;
else
section.platforms[i].type =
(rand() % 2) ? PLATFORM_EMPTY : PLATFORM_WHITE;
}
level->section_buffer.push_back(section);
srand(123456);
}
void gen2::generate(struct level *level)
{
struct platform p;
if(m_path_carver.next(SECTION_LENGTH, &p)) {
level->platform_buffer.push_back(p);
}
}

View File

@ -4,29 +4,17 @@
#define N PLATFORM_COUNT
gen3::gen3() : last_pos{0, 1, 2} { srand(123456); }
void gen3::generate(level_t *level)
gen3::gen3(): m_path_carvers{{0}, {N/3}, {2*N/3}}
{
struct section section;
for (int i = 0; i < 3; ++i) {
this->last_pos[i] += (rand() % 3) - 1;
if (this->last_pos[i] < 0)
this->last_pos[i] = N - 1;
if (this->last_pos[i] >= N)
this->last_pos[i] = 0;
}
for (int i = 0; i < N; ++i) {
section.platforms[i].type = PLATFORM_EMPTY;
if (i == this->last_pos[0]
|| i == this->last_pos[1]
|| i == this->last_pos[2]
) {
section.platforms[i].type = PLATFORM_WHITE;
}
}
level->section_buffer.push_back(section);
srand(123456);
}
void gen3::generate(struct level *level)
{
struct platform p;
for(int i = 0; i < 3; i++) {
if(m_path_carvers[i].next(SECTION_LENGTH, &p)) {
level->platform_buffer.push_back(p);
}
}
}

View File

@ -1,22 +1,32 @@
#include "../generator.h"
#include "../level.h"
path::path(int initial_pos):
pos {initial_pos}, last_skipped {true}
path_carver::path_carver(int initial_face)
{
m_face = initial_face;
m_last_skipped = true;
m_z = num(0);
}
void path::next(struct section *section)
bool path_carver::next(num length, struct platform *p)
{
int skip = !this->last_skipped && (rand() % 4 == 0);
int skip = !m_last_skipped && (rand() % 4 == 0);
if(skip) {
this->last_skipped = true;
m_last_skipped = true;
m_z += length;
return false;
}
else {
p->face = m_face;
p->z = m_z;
p->length = length;
p->type = PLATFORM_WHITE;
int diff = rand() % 3 - 1;
section->platforms[this->pos].type = PLATFORM_WHITE;
this->pos = (this->pos + PLATFORM_COUNT + diff) % PLATFORM_COUNT;
this->last_skipped = false;
m_face = (m_face + PLATFORM_COUNT + diff) % PLATFORM_COUNT;
m_z += length;
m_last_skipped = false;
return true;
}
}

View File

@ -2,58 +2,27 @@
#include "generator.h"
#include <gint/display.h>
level_t level_create(int level)
struct level level_create(int level)
{
level_t l;
struct level l;
switch(level) {
case 2:
l.gen = std::make_unique<struct gen2>();
break;
case 3:
l.gen = std::make_unique<struct gen3>();
break;
default:
l.gen = std::make_unique<struct gen1>();
}
return l;
switch(level) {
case 2:
l.gen = std::make_unique<struct gen2>();
break;
case 3:
l.gen = std::make_unique<struct gen3>();
break;
default:
l.gen = std::make_unique<struct gen1>();
}
return l;
}
void level_update(level_t *level)
void level_update(struct level *level, num z)
{
while (level->section_buffer.size() < LEVEL_BUFFER_LENGTH) {
level->gen->generate(level);
}
}
void level_advance(level_t *level)
{
if(level->section_buffer.size())
level->section_buffer.erase(level->section_buffer.begin());
}
void level_display(level_t *level)
{
int w;
int h;
int sx;
int sy;
dsize("@", NULL, &w, &h);
sx = (DWIDTH - (PLATFORM_COUNT * w)) / 2;
sy = (DHEIGHT - (LEVEL_BUFFER_LENGTH * h)) / 2;
drect_border(
sx, sy,
sx + (PLATFORM_COUNT * w), sy + (LEVEL_BUFFER_LENGTH * h),
C_NONE, 2, C_BLACK
);
for (int i = 4; i >= 0; --i) {
for (int j = 0; j < PLATFORM_COUNT; ++j) {
if (level->section_buffer[i].platforms[j].type != PLATFORM_EMPTY)
dtext(sx + (w * j), sy + (h * i), C_BLACK, "@");
}
}
while(!level->platform_buffer.size()
|| level->platform_buffer[level->platform_buffer.size() - 1].z
< z + RENDER_DEPTH)
level->gen->generate(level);
}

View File

@ -14,27 +14,22 @@ typedef enum {
} platform_type_t;
struct platform {
int face;
num z;
num length;
platform_type_t type;
//TODO blink ? meta...
};
struct section {
struct platform platforms[PLATFORM_COUNT];
//...
};
struct generator;
typedef struct level {
std::vector<struct section> section_buffer;
struct level {
std::vector<struct platform> platform_buffer;
std::unique_ptr<struct generator> gen;
} level_t;
};
extern void level_update(level_t *level);
extern level_t level_create(int level);
extern void level_display(level_t *level);
extern void level_advance(level_t *level);
extern void level_update(struct level *level, num z);
extern struct level level_create(int level);
#endif /* __LEVEL_H__ */

View File

@ -68,7 +68,7 @@ int play_level(int level_id)
prof_t perf_frame = prof_make();
prof_enter_norec(perf_frame);
level_update(&game.level);
level_update(&game.level, player->z);
camera_track(camera, player);
//======= Rendering =======//
@ -79,41 +79,22 @@ int play_level(int level_id)
prof_t perf_comp = prof_make();
prof_enter_norec(perf_comp);
for(int depth = RENDER_DISTANCE - 1; depth >= 0; depth--) {
num z = (game.sections_passed + depth) * SECTION_LENGTH;
struct section *s = &game.level.section_buffer[depth];
for(auto it = game.level.platform_buffer.rbegin();
it != game.level.platform_buffer.rend();
++it) {
int color = camera_platform_color(&game.camera, it->face);
struct prect p =
space_platform_position(it->face, it->z, it->length);
for(int i = 0; i < PLATFORM_COUNT; i++) {
/* Set this to get the full tunnel test */
constexpr bool full_tunnel = false;
/* Set this to get the OG full tunnel coloring */
constexpr bool og_coloring = false;
/* Near plane clipping */
num near = camera->pos.z + camera->near_plane();
if(p.fl.z <= near) continue;
if(p.nl.z <= near) p.nl.z = near;
if(p.nr.z <= near) p.nr.z = near;
camera_project_prect(&game.camera, &p);
int color = C_WHITE;
if(!full_tunnel && s->platforms[i].type != PLATFORM_WHITE)
continue;
if(og_coloring) {
int j = i + depth + game.sections_passed;
int gray = (j % PLATFORM_COUNT) * 31 / PLATFORM_COUNT;
color = C_RGB(gray, gray, gray);
}
else {
color = camera_platform_color(&game.camera, i);
}
struct prect p = space_platform_position(i, z);
/* Near plane clipping */
num near = camera->pos.z + camera->near_plane();
if(p.fl.z <= near) continue;
if(p.nl.z <= near) p.nl.z = near;
if(p.nr.z <= near) p.nr.z = near;
camera_project_prect(&game.camera, &p);
render_triangle(&p.nl, &p.fr, &p.fl, color);
render_triangle(&p.fr, &p.nl, &p.nr, color);
}
render_triangle(&p.nl, &p.fr, &p.fl, color);
render_triangle(&p.fr, &p.nl, &p.nr, color);
}
/* Render player */
@ -151,15 +132,15 @@ int play_level(int level_id)
game.debug.footer = !game.debug.footer;
if(key == KEY_F1) {
game.level = level_create(1);
level_update(&game.level);
level_update(&game.level, game.player.z);
}
if(key == KEY_F2) {
game.level = level_create(2);
level_update(&game.level);
level_update(&game.level, game.player.z);
}
if(key == KEY_F3) {
game.level = level_create(3);
level_update(&game.level);
level_update(&game.level, game.player.z);
}
if(key == KEY_LEFT && !player->airborne()) {
player->stance = player::Jumping;
@ -191,6 +172,7 @@ int play_level(int level_id)
//======= Simulation =======//
num old_height = player->height;
player->z += player->vz * dt;
player->height += player->vheight * dt;
@ -224,7 +206,7 @@ int play_level(int level_id)
}
/* TODO: Reset jump after contact on platforms, not distance */
if(player->height <= 0) {
if(old_height > 0 && player->height <= 0) {
player->stance = player::Running;
player->jump_dir = 0;
player->jump_t = 0.0;
@ -235,15 +217,7 @@ int play_level(int level_id)
}
}
/* Eliminate dead platforms so new ones can be generated. We include
the camera's distance in the calculation because we only remove
platforms that have been passed *and* are now invisible. */
while(player->z - RENDER_CAMERA_BACK_DISTANCE >
(game.sections_passed + 1) * SECTION_LENGTH) {
level_advance(&game.level);
game.sections_passed++;
}
game_advance(&game);
prof_leave_norec(perf_frame);
last_frame_us = prof_time(perf_frame);
}
@ -258,7 +232,7 @@ int main(void)
__printf_enable_fp();
prof_init();
game_init();
space_init();
extern font_t font_boson;
dfont(&font_boson);

View File

@ -1,9 +1,13 @@
#ifndef __SETTINGS_H__
# define __SETTINGS_H__
#include <num/num.h>
using namespace libnum;
/* Radius of the level cylinder, in world units. */
#define LEVEL_RADIUS num(4.0)
/* Section lengths, in world units. */
/* Section lengths, in world units.
TODO: Remove, we don't use sections anymore */
#define SECTION_LENGTH num(3.0)
/* Number of platforms in the world cylinder. */
#define PLATFORM_COUNT 10
@ -27,9 +31,8 @@
#define RENDER_DISTANCE 8
/* Distance between the player and the camera */
#define RENDER_CAMERA_BACK_DISTANCE num(0.67)
/* Number of level segments that are guaranteed generated ahead-of-time. */
#define LEVEL_BUFFER_LENGTH 12
/* Depth at which we start rendering platforms */
#define RENDER_DEPTH (num(8.0) * SECTION_LENGTH)
/* Set to 1 to enable logging by USB. */
#define LOG_USB_ENABLE 0