lv2 + platform height + death conditions

This commit is contained in:
Lephenixnoir 2023-05-24 18:15:39 +02:00
parent 4f38cd911b
commit 19caa845de
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
11 changed files with 252 additions and 32 deletions

View File

@ -96,19 +96,29 @@ void space_init(void)
platform_distance = LEVEL_RADIUS * num_cos(arc / 2);
}
struct prect space_platform_position(int face, num z, num length)
num space_platform_dist_from_center(int height_value)
{
return LEVEL_RADIUS * (num(12 - height_value) / 12);
}
num space_platform_height(int height_value)
{
return LEVEL_RADIUS - space_platform_dist_from_center(height_value);
}
struct prect space_platform_position(struct platform const *p)
{
struct prect r;
vec3 radius(0, -LEVEL_RADIUS, z);
vec3 radius(0, -space_platform_dist_from_center(p->height), p->z);
int angle_l = face;
int angle_r = (face + 1) % PLATFORM_COUNT;
int angle_l = p->face;
int angle_r = (p->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 += length;
r.fr.z += length;
r.fl.z += p->length;
r.fr.z += p->length;
return r;
}
@ -130,10 +140,33 @@ void game_advance(struct game *game)
}
}
num game_height_at(struct game *game, num z, int face)
{
int max_h = -1;
for(auto const &p: game->level.platform_buffer) {
if(p.face == face && z >= p.z && z <= p.z + p.length)
max_h = (max_h >= p.height) ? max_h : p.height;
}
return max_h >= 0
? space_platform_height(max_h)
: -KILL_PLANE_RADIUS;
}
#if 0
bool game_player_above_platform(struct game *game, num height)
{
struct player const *player = &game->player;
struct level const *level = &game->level;
for(auto const &p: game->level.platform_buffer) {
if(player->platform == p.face
&& player->z >= p.z && player->z <= p.z + p.length
&& height >= space_platform_height(p.height)) {
return true;
}
}
return false;
}
#endif

View File

@ -98,20 +98,28 @@ consteval num space_platform_arc()
return num(2 * 3.14159 / PLATFORM_COUNT);
}
/* Height (in world coordinates in the range [-LEVEL_RADIUS..0]) of a platform
whose height value in the `struct platform` is given as parameter. */
num space_platform_height(int height_value);
/* 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 face
(0..PLATFORM_COUNT-1) that has its near side at the specified depth z. */
struct prect space_platform_position(int face, num z, num length);
/* Position, in world units, of the provided flat platform. */
struct prect space_platform_position(struct platform const *p);
//======= Game management =======//
/* Advance in the level by discarding platforms that have been passed. */
void game_advance(struct game *game);
/* Get platform height at the provided position. */
num game_height_at(struct game *game, num z, int face);
#if 0
/* 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
#endif /* __GAME_H__ */

View File

@ -19,6 +19,8 @@ struct path_carver
*p, false otherwise (in that case don't use *p). If force is set, always
generates a platform. */
bool next(num length, struct platform *p, bool force=false);
/* Teleport to the specified z. */
void teleport(num z);
private:
/* Face where the next platform will be generated */
@ -47,14 +49,14 @@ static inline int prev_face(int face)
//======= Level generator API =======//
struct generator
struct Generator
{
/* Generate more stuff. */
virtual void generate(struct level *) = 0;
virtual ~generator() = default;
virtual ~Generator() = default;
};
struct gen1 : public generator
struct gen1: public Generator
{
gen1();
void generate(struct level *) override;
@ -64,16 +66,18 @@ struct gen1 : public generator
path_carver m_path_carvers[3];
};
struct gen2 : public generator
struct AcceleronGenerator: public Generator
{
gen2();
AcceleronGenerator();
void generate(struct level *) override;
~gen2() override = default;
~AcceleronGenerator() override = default;
num m_z;
bool m_initial;
path_carver m_path_carver;
};
struct gen3 : public generator
struct gen3: public Generator
{
gen3();
void generate(struct level *) override;

View File

@ -1,16 +1,147 @@
#include "../generator.h"
#include "../level.h"
#include "../util.h"
#include <stdlib.h>
gen2::gen2(): m_path_carver{0}
/* Geometric parameters */
#define G_PLATFORM_LEN num(3.0)
#define G_PLATFORM_SPC num(0.5)
#define G_PLATFORM_LEN_INITIAL num(6.0)
/* Probabilities */
#define P_ASCF_RED num(0.2)
AcceleronGenerator::AcceleronGenerator(): m_path_carver{0}
{
srand(123456);
m_initial = true;
m_z = 0.0;
}
void gen2::generate(struct level *level)
/* 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]
....|.....
....|.....
...B|..... [h=5]
...|.#.... [h=4]
...|.|....
....#..... [h=1]
Returns updated z. */
static num generate_ascending_funnel(struct level *level, num z)
{
struct platform p;
if(m_path_carver.next(SECTION_LENGTH, &p)) {
level->platform_buffer.push_back(p);
/* 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;
if(num_rand() <= P_ASCF_RED)
p.type = PLATFORM_RED;
level->platform_buffer.push_back(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;
level->platform_buffer.push_back(p);
p.face = (i & 1) ? 3 : 5;
level->platform_buffer.push_back(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;
level->platform_buffer.push_back(p);
p.type = PLATFORM_BLUE;
p.face = 3;
p.z = z + G_PLATFORM_LEN * 2;
p.length = G_PLATFORM_LEN * 3;
p.height = 5;
level->platform_buffer.push_back(p);
p.type = PLATFORM_WHITE;
p.face = 5;
p.z = z + G_PLATFORM_LEN * 3;
p.length = G_PLATFORM_LEN * 2;
p.height = 4;
level->platform_buffer.push_back(p);
z += G_PLATFORM_LEN * 5 + G_PLATFORM_SPC;
/* Exit platform */
p.type = PLATFORM_WHITE;
p.face = 4;
p.z = z;
p.length = G_PLATFORM_LEN;
p.height = 0;
level->platform_buffer.push_back(p);
z += G_PLATFORM_LEN + G_PLATFORM_SPC;
return z;
}
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;
level->platform_buffer.push_back(p);
m_z += p.length;
m_initial = false;
}
m_z = generate_ascending_funnel(level, m_z);
/* 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 #4: Checkerboard random paths */
/* Phase #5: Obstacles
- ##.##.#.#.
- .##.##.#.#
- ..#..#..#.
- .#..#..#.. */
}

View File

@ -30,3 +30,8 @@ bool path_carver::next(num length, struct platform *p, bool force)
return true;
}
}
void path_carver::teleport(num z)
{
m_z = z;
}

View File

@ -8,7 +8,7 @@ struct level level_create(int level)
switch(level) {
case 2:
l.gen = std::make_unique<struct gen2>();
l.gen = std::make_unique<AcceleronGenerator>();
break;
case 3:
l.gen = std::make_unique<struct gen3>();

View File

@ -17,14 +17,15 @@ struct platform {
int face;
num z;
num length;
int height;
platform_type_t type;
};
struct generator;
struct Generator;
struct level {
std::vector<struct platform> platform_buffer;
std::unique_ptr<struct generator> gen;
std::unique_ptr<Generator> gen;
};

View File

@ -83,8 +83,7 @@ int play_level(int level_id)
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);
struct prect p = space_platform_position(&*it);
/* Near plane clipping */
num near = camera->pos.z + camera->near_plane();
@ -108,13 +107,22 @@ int play_level(int level_id)
prof_leave_norec(perf_comp);
azrp_update();
num floor = 0;
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);
}
@ -172,7 +180,6 @@ int play_level(int level_id)
//======= Simulation =======//
num old_height = player->height;
player->z += player->vz * dt;
player->height += player->vheight * dt;
@ -185,6 +192,7 @@ int play_level(int level_id)
gravity = FALLING_GRAVITY;
}
player->vheight += gravity * dt;
floor = game_height_at(&game, player->z, player->platform);
if(player->airborne()) {
player->jump_t += dt;
@ -205,17 +213,30 @@ int play_level(int level_id)
player->vheight = 0.0;
}
/* TODO: Reset jump after contact on platforms, not distance */
if(old_height > 0 && player->height <= 0) {
if(player->height >= floor - STEP_HEIGHT
&& player->height <= floor) {
player->stance = player::Running;
player->jump_dir = 0;
player->jump_t = 0.0;
player->height = 0;
player->height = floor;
player->vheight = 0;
player->jump_key = 0;
player->frame = 0;
}
}
else if(floor < player->height) {
player->stance = player::Falling;
player->vheight = 0.0;
}
/* Death condition #1/2: inside a platform (hit it in front) */
bool death_1 = (player->height >= floor - PLAYER_HEIGHT &&
player->height < floor - STEP_HEIGHT);
/* Death condition #2/2: below the kill plane */
bool death_2 = (player->height < LEVEL_RADIUS - KILL_PLANE_RADIUS);
if(death_1 || death_2)
break;
game_advance(&game);
prof_leave_norec(perf_frame);
@ -257,7 +278,7 @@ int main(void)
continue;
}
while (menu_select_level(&level) >= 0) {
play_level(level);
play_level(level + 1);
}
}
return 0;

View File

@ -6,6 +6,8 @@ using namespace libnum;
/* Radius of the level cylinder, in world units. */
#define LEVEL_RADIUS num(4.0)
/* Radius of the kill plane */
#define KILL_PLANE_RADIUS num(10.0)
/* Section lengths, in world units.
TODO: Remove, we don't use sections anymore */
#define SECTION_LENGTH num(3.0)
@ -20,6 +22,10 @@ using namespace libnum;
#define FALLING_GRAVITY num(-8.0)
/* 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))
/* Player height, for collisions with platform fronts. */
#define PLAYER_HEIGHT num(0.8)
/* Duration of a rotation by one platform, in seconds. */
#define TIME_ROTATION num(0.1)

View File

@ -1,6 +1,7 @@
#include "util.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
static num num_cos_dl(num a)
{
@ -33,6 +34,13 @@ num num_clamp(num t, num lower, num upper)
return t;
}
num num_rand(void)
{
num r;
r.v = rand() & 0xffff;
return r;
}
vec3 vec_rotate_around_z(vec3 v, vec2 rotator)
{
num c = rotator.x;

View File

@ -19,6 +19,9 @@ num num_sin(num a);
/* Clamp a num between two bounds. */
num num_clamp(num t, num lower_bound, num upper_bound);
/* Random num between 0 and 1. */
num num_rand(void);
/* String representation of various objects (static strings rotating; can use
up to 8 of them at once). */
char const *str(num x);