lv2: add a random checkerboard path phase

This commit is contained in:
Lephenixnoir 2023-05-24 19:59:36 +02:00
parent f8a0544b87
commit 08411fc90c
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
6 changed files with 141 additions and 13 deletions

View File

@ -31,6 +31,45 @@ private:
bool m_last_skipped;
};
/* A path carver that generates multiple simultaneous paths following a
grid-like structure (platforms all of the same length, minus some random
variation). */
struct MultiPathCarver
{
/* Create a carver with a set number of parallel paths. */
MultiPathCarver(int N);
/* Set initial positions. */
void set_faces(int *faces /* size N */);
/* Force the carver to generate all platforms and never skip. */
void set_noskip(bool noskip);
/* Force the carver to generate paths along a checkerboard. */
void set_checkerboard(bool checkerboard);
/* Rotate the current state to force one platform to be at `face`. */
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);
private:
/* Number of parallel paths */
int m_N;
/* Information specific to each path */
struct {
/* Current face */
int face;
/* Whether last platform was skipped */
bool last_skipped;
} *m_paths;
/* Force all platforms to be generated */
bool m_noskip;
/* Force a checkerboard pattern */
bool m_checkerboard;
};
static inline int add_to_face(int face, int how_much)
{
int f = (face + how_much) % PLATFORM_COUNT;
@ -75,7 +114,8 @@ struct AcceleronGenerator: public Generator
num m_z;
bool m_initial;
path_carver m_path_carver;
int m_phase;
MultiPathCarver m_checkerboard_carver;
};
struct gen3: public Generator

View File

@ -10,11 +10,18 @@
/* Probabilities */
#define P_ASCF_RED num(0.2)
AcceleronGenerator::AcceleronGenerator(): m_path_carver{0}
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
@ -128,7 +135,21 @@ void AcceleronGenerator::generate(struct level *level)
m_initial = false;
}
m_z = generate_ascending_funnel(level, m_z);
/* Phase #0: Ascending funnel and long/tight red platform */
if(m_phase == 0) {
m_z = generate_ascending_funnel(level, m_z);
m_checkerboard_carver.force_to_match(4);
m_phase = 1;
}
/* 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;
}
m_phase = 0;
}
/* Phase #1: Almost-full tunnel with random holes? */
@ -136,7 +157,6 @@ void AcceleronGenerator::generate(struct level *level)
/* Phase #3: Full + odd + even tunnel combination leading into #0 */
/* Phase #4: Checkerboard random paths */
/* Phase #5: Obstacles
- ##.##.#.#.

View File

@ -36,3 +36,78 @@ void path_carver::teleport(num z)
{
m_z = z;
}
//~
MultiPathCarver::MultiPathCarver(int N)
{
m_N = N;
m_paths = static_cast<decltype(m_paths)>(malloc(N * sizeof *m_paths));
m_noskip = false;
m_checkerboard = false;
for(int i = 0; i < N; i++)
m_paths[i].last_skipped = false;
}
void MultiPathCarver::set_faces(int *faces)
{
for(int i = 0; i < m_N; i++)
m_paths[i].face = faces[i];
}
void MultiPathCarver::set_noskip(bool noskip)
{
m_noskip = noskip;
}
void MultiPathCarver::set_checkerboard(bool checkerboard)
{
m_checkerboard = checkerboard;
}
void MultiPathCarver::force_to_match(int face)
{
if(m_N <= 0)
return;
int diff = face - m_paths[0].face;
for(int i = 0; i < m_N; i++) {
m_paths[i].face = add_to_face(m_paths[i].face, diff);
}
}
void MultiPathCarver::next(struct level *level, num z, num length)
{
/* Decide whether to skip */
for(int i = 0; i < m_N; i++) {
bool skip = !m_noskip && !m_paths[i].last_skipped && (rand() % 4 == 0);
m_paths[i].last_skipped = skip;
}
/* Generate platforms for the current positions */
for(int face = 0; face < PLATFORM_COUNT; face++) {
bool is_hit = false;
for(int i = 0; i < m_N; i++)
is_hit |= (!m_paths[i].last_skipped && m_paths[i].face == face);
if(is_hit) {
struct platform p;
p.face = face;
p.z = z;
p.length = length;
p.height = 0;
p.type = PLATFORM_WHITE;
level->platform_buffer.push_back(p);
}
}
/* Advance positions */
for(int i = 0; i < m_N; i++) {
/* In checkerboard mode, only allow diff -1/+1 instead of -1/0/+1 */
int diff = m_checkerboard
? 2 * (rand() % 2) - 1
: rand() % 3 - 1;
m_paths[i].face = add_to_face(m_paths[i].face, diff);
}
}

View File

@ -111,18 +111,11 @@ int play_level(int level_id)
if(game.debug.footer) {
drect(0, DHEIGHT-20, DWIDTH-1, DHEIGHT-1, C_WHITE);
#if 0
dprint(4, 209, C_BLACK, "render:%4d+%4dus comp:%4dus FPS:%02d",
prof_time(azrp_perf_render) - prof_time(azrp_perf_r61524),
prof_time(azrp_perf_r61524),
prof_time(perf_comp),
last_frame_us ? 1000000 / last_frame_us : 0);
#else
dprint(4, 209, C_BLACK, "floor:%f h:%f vh:%f",
(double)floor,
(double)game.player.height,
(double)game.player.vheight);
#endif
r61524_display(gint_vram, DHEIGHT-20, 20, R61524_DMA_WAIT);
}

View File

@ -20,7 +20,7 @@ struct camera
/* Distance to the near plane, for clipping.
TODO: Figure out a better choice of a near plane. */
num near_plane() const { return this->screen_distance() * num(0.75); }
num near_plane() const { return this->screen_distance() * num(0.25); }
//======= World data =======//

View File

@ -23,7 +23,7 @@ using namespace libnum;
/* Vertical speed granted when a jump starts (world units/s). */
#define JUMP_THRUST num(1.5)
/* Maximum height of a step that can be crossed without dying. */
#define STEP_HEIGHT (LEVEL_RADIUS * num(0.12))
#define STEP_HEIGHT (LEVEL_RADIUS * num(0.09))
/* Player height, for collisions with platform fronts. */
#define PLAYER_HEIGHT num(0.8)