finish tutorial

This commit is contained in:
Lephenixnoir 2023-04-23 15:54:45 +02:00
parent b9067833bc
commit 15982b079f
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
9 changed files with 298 additions and 87 deletions

View File

@ -17,7 +17,9 @@ if(AZUR_PLATFORM STREQUAL gint)
src/backend/gint/shader.cc)
list(APPEND ASSETS
assets/font.png
assets/1_1.txt)
assets/1_1.txt
assets/1_2.txt
assets/1_3.txt)
endif()
if(AZUR_PLATFORM STREQUAL linux)

View File

@ -1,13 +1,13 @@
name: 1-1
finish: 66
finish: 67
1-3: text "Welcome to the drop!\n"
4-8: text "I know you have\nmany questions.\n\nDon't ask them. We're busy."
9-13: text "Use the arrow keys to move."
20-24: text "Red bad.\nDon't touch the red stuff."
26: plane DAMAGE 0b10001'10001'10001'10001'10001
30: plane DAMAGE 0b11111'10001'10001'10001'11111
26: plane DAMAGE VLINES_1_5
30: plane DAMAGE BOX
34: plane DAMAGE 0b11100'11100'11100'00000'00000
38: plane DAMAGE 0b11100'11000'10001'00011'00111
@ -17,5 +17,5 @@ finish: 66
50: plane DAMAGE 0b00111'00111'00111'00111'00111
52: plane DAMAGE 0b11111'11111'11111'00000'00000
56-60: text "Nice job.\n\nDon't worry, I'll teach you\nfrom the ground up."
62-64: text "Get it? The ground. Haha."
56-62: text "Nice job.\n\nDon't worry, I'll teach you\nhow to deal with the drop\nfrom the ground up."
63-65: text "Get it? The ground. Haha."

26
assets/1_2.txt Normal file
View File

@ -0,0 +1,26 @@
name: 1-2
finish: 53
1-5: text "Press F1 or F2 to roll."
7: plane DAMAGE 0b01110'11111'11011'10001'00000
10: plane DAMAGE 0b01110'00111'00011'00111'01110
13: plane DAMAGE 0b00000'10001'11011'11111'01110
16: plane DAMAGE 0b01110'11100'11000'11100'01110
18-21: text "If you roll while close\nto an edge, you will\nmove along the wall."
24: plane DAMAGE 0b10001'11111'11111'11111'11111
26: plane DAMAGE 0b11111'01111'01111'01111'11111
28: plane DAMAGE 0b11111'11111'11111'11111'10001
30: plane DAMAGE 0b11111'11110'11110'11110'11111
34-38: text "Green good.\nRoll to make the arrow point down."
40: plane ARROW ARROW_+X
42: plane ARROW ARROW_-X
44: plane DAMAGE BOX
45: plane ARROW ARROW_+Z
46: plane ARROW ARROW_-Z
48-51: text "Impressive! You're taking off!"

20
assets/1_3.txt Normal file
View File

@ -0,0 +1,20 @@
name: 1-3
finish: 36
1-4: text "Oh, we have mirrors too.\nAbsolutely not for safety reasons."
3-33: mirror
6: plane DAMAGE BOX
8: plane DAMAGE BIGDOT
10: plane DAMAGE BOX_DOT
12-15: text "Ok, I lied.\n\nThere MIGHT be invisible red stuff."
18: plane INVIS BOX
20: plane INVIS BIGDOT
22: plane ARROW ARROW_+X
24: plane ARROW ARROW_-X
26: plane INVIS BOX_DOT
28-30: text "Wow, you're good.\nYou're gonna run the lab\ninto the ground."
32-34: text "The DROP. *"

4
assets/2_1.txt Normal file
View File

@ -0,0 +1,4 @@
name: 2-1
finish: 10
1-4: text "You don't have a reflection\nin the mirror.\n\nWhat does that tell us\nabout you?"

View File

@ -24,7 +24,7 @@ def convert_level(input, params):
}
RE_ELEMENTS = {
"mirror": (True, re.compile(r'')),
"plane": (False, re.compile(r'([A-Z]+)\s+(\S+)')),
"plane": (False, re.compile(r'(\S+)\s+(\S+)')),
"text": (True, re.compile(r'\"([^"]+)\"')),
}
RE_POSITION = re.compile(r'(\d+)(?:-(\d+))?')
@ -84,22 +84,43 @@ def convert_level(input, params):
elif key == "plane":
kind = m[1]
shape = m[2]
shape_str = m[2]
assert shape.startswith("0b")
shape = "".join(c for c in shape[2:] if c != "'")
assert(len(shape) == 25)
predef_shapes = {
"VLINES_1_5": 0b1000110001100011000110001,
"BOX": 0b1111110001100011000111111,
"BOX_DOT": 0b1111110001101011000111111,
"BIGDOT": 0b0000001110011100111000000,
"ARROW_+X": 0b0010000010111110001000100,
"ARROW_-X": 0b0010001000111110100000100,
"ARROW_+Z": 0b0010001110101010010000100,
"ARROW_-Z": 0b0010000100101010111000100,
}
if shape_str in predef_shapes:
shape = predef_shapes[shape_str]
else:
assert shape_str.startswith("0b")
shape = "".join(c for c in shape_str[2:] if c != "'")
assert(len(shape) == 25)
shape = int(shape, 2)
kinds = {
"DAMAGE": 0,
"ARROW": 1,
"INVIS": 2,
}
assert kind in kinds
data = 0
if kind == "ARROW":
directions = ["ARROW_-Z", "ARROW_+X", "ARROW_+Z", "ARROW_-X"]
assert shape_str in directions
data = directions.index(shape_str)
planes += num(start * 32)
planes += fxconv.u32(int(shape, 2))
planes += fxconv.u32(shape)
planes += fxconv.u16(kinds[kind])
planes += fxconv.u16(0) # TODO: Arrow directions
planes += fxconv.u16(data)
plane_count += 1
elif key == "text":

View File

@ -55,6 +55,7 @@ struct element_mirror {
enum element_plane_type {
ELEMENT_PLANE_DAMAGE,
ELEMENT_PLANE_ARROW,
ELEMENT_PLANE_INVIS,
};
struct element_plane {
num y;
@ -103,6 +104,8 @@ void render_fragment(struct camera const *camera, struct world const *world,
bool camera_rolling(struct camera const *camera);
void camera_roll(struct camera *camera, float snap_factor);
bool plane_collides(num16 x, num16 z, uint32_t shape);
//=== Azur shader wrapping the raytracing ===//
void cd_raytrace(struct camera const *camera, struct world const *world);
@ -116,7 +119,11 @@ struct input {
bool up;
bool down;
bool OPTN;
bool roll;
bool roll_left;
bool roll_right;
bool RESET_LEVEL;
bool PREV_LEVEL;
bool NEXT_LEVEL;
};
//=== Additions to libnum ===//

View File

@ -73,7 +73,8 @@ static int platform_update(struct input *input)
input->down = state[SDL_SCANCODE_DOWN];
input->left = state[SDL_SCANCODE_LEFT];
input->right = state[SDL_SCANCODE_RIGHT];
input->roll = state[SDL_SCANCODE_RETURN];
input->roll_left = state[SDL_SCANCODE_F1];
input->roll_right = state[SDL_SCANCODE_F2];
return 0;
}
@ -126,8 +127,16 @@ static int platform_update(struct input *input)
gint_osmenu();
if(e.key == KEY_OPTN)
input->OPTN = true;
if(e.key == KEY_SHIFT)
input->roll = true;
if(e.key == KEY_F1)
input->roll_left = true;
if(e.key == KEY_F2)
input->roll_right = true;
if(e.key == KEY_EXE && keydown(KEY_VARS))
input->RESET_LEVEL = true;
if(e.key == KEY_MINUS && keydown(KEY_VARS))
input->PREV_LEVEL = true;
if(e.key == KEY_PLUS && keydown(KEY_VARS))
input->NEXT_LEVEL = true;
}
input->left = keydown(KEY_LEFT);
@ -147,6 +156,7 @@ static void init(void)
prof_init();
azrp_config_scale(2);
cd_raytrace_configure();
azrp_shader_clear_configure();
azrp_shader_image_p8_configure();
}
@ -227,8 +237,8 @@ vec3 operator * (mat3 const &M, vec3 const &u)
return v;
}
#if 0
static void print_text(int x, int y, char const *fmt, ...)
__attribute__((maybe_unused))
static void print_text(int x, int y, char const *fmt, ...)
{
static char str[128];
va_list args;
@ -237,7 +247,6 @@ static void print_text(int x, int y, char const *fmt, ...)
va_end(args);
draw_text(x, y, str, strlen(str));
}
#endif
void render_centered_text(char const *str)
{
@ -263,16 +272,99 @@ void render_centered_text(char const *str)
static struct camera *camera = NULL;
static struct world *world = NULL;
static struct level *level = NULL;
static int reset_frames = 0;
static bool debug = false;
#ifdef AZUR_TOOLKIT_GINT
extern struct level level_1_1, level_1_2, level_1_3;
static struct level const *levels[] = {
&level_1_1, &level_1_2, &level_1_3,
};
#else
/* Basic basic level */
static struct level const lv_test = {
.name = "<Test>",
.finish = num(1024),
.mirror_count = 1,
.mirrors = (struct element_mirror[]){
{ .begin = 256, .end = 768 },
},
.plane_count = 2,
.planes = (struct element_plane[]){
{ .y = 384, .shape = 0b01010'10101'01010'10101'01010,
.type = ELEMENT_PLANE_DAMAGE, .data = 0 },
{ .y = 384+64, .shape = 0b11111'10001'10001'10001'11111,
.type = ELEMENT_PLANE_DAMAGE, .data = 0 },
},
.text_count = 1,
.texts = (struct element_text[]){
{ .begin = 32, .end = 256,
.str = "Red stuff bad!\nDon't touch the red stuff!", },
},
};
static struct level const *levels[] = { &lv_test };
#endif
static int current_level = 0;
void load_level_number(int number)
{
int total_number = sizeof levels / sizeof levels[0];
if(number < 0)
number = 0;
if(number > total_number)
number = total_number - 1;
current_level = number;
}
bool is_final_level(int number)
{
int total_number = sizeof levels / sizeof levels[0];
return number >= total_number - 1;
}
void world_reset(struct world *world)
{
world->neon_position = 0;
world->depth = 0;
world->neon_period = 64;
world->mirror = NULL;
world->plane = NULL;
world->text = NULL;
world->mirror_index = 0;
world->plane_index = 0;
world->text_index = 0;
}
void world_level_transition(struct world *world)
{
world->depth = world->depth % num(world->neon_period);
world->mirror = NULL;
world->plane = NULL;
world->text = NULL;
world->mirror_index = 0;
world->plane_index = 0;
world->text_index = 0;
}
void render(void)
{
struct level const *level = levels[current_level];
#ifdef AZUR_TOOLKIT_GINT
azrp_perf_clear();
cd_raytrace(camera, world);
if(world->text)
render_centered_text(world->text->str);
if(reset_frames > 0) {
reset_frames--;
azrp_clear(0xffff);
}
else {
cd_raytrace(camera, world);
if(world->text)
render_centered_text(world->text->str);
draw_text(1, 1, level->name, strlen(level->name));
}
platform_render();
if(debug) {
@ -283,7 +375,16 @@ void render(void)
r61524_display(gint_vram, DHEIGHT-20, 20, R61524_DMA_WAIT);
}
#else
render_fragment(camera, world, vram, 0, VHEIGHT);
if(reset_frames > 0) {
reset_frames--;
memset(vram, 0xff, sizeof vram);
}
else {
render_fragment(camera, world, vram, 0, VHEIGHT);
if(world->text)
render_centered_text(world->text->str);
draw_text(1, 1, level->name, strlen(level->name));
}
platform_render();
#endif
}
@ -293,8 +394,26 @@ int update(void)
struct input input = {};
if(platform_update(&input))
return 1;
/* Debug contros */
if(input.OPTN)
debug = !debug;
if(input.RESET_LEVEL) {
world_reset(world);
reset_frames = 2;
}
if(input.NEXT_LEVEL && !is_final_level(current_level)) {
world_level_transition(world);
load_level_number(current_level+1);
reset_frames = 2;
}
if(input.PREV_LEVEL && current_level > 0) {
world_level_transition(world);
load_level_number(current_level-1);
reset_frames = 2;
}
if(reset_frames > 0)
return 0;
static num const mv_speed = 0.45;
static num const wall = WORLD_SIZE * 0.45; /* margin */
@ -331,10 +450,10 @@ int update(void)
camera->pos.x = clamp(camera->pos.x, -wall, wall);
camera->pos.z = clamp(camera->pos.z, -wall, wall);
if(input.roll && input.left && !camera_rolling(camera)) {
if(input.roll_left && !camera_rolling(camera)) {
camera->roll_quadrant = (camera->roll_quadrant + 4 - 1) % 4;
}
else if(input.roll && input.right && !camera_rolling(camera)) {
else if(input.roll_right && !camera_rolling(camera)) {
camera->roll_quadrant = (camera->roll_quadrant + 1) % 4;
}
@ -352,7 +471,19 @@ int update(void)
if(w->mirror && w->depth > w->mirror->end)
w->mirror = NULL;
if(w->plane && w->depth > w->plane->y - num(4)) {
// TODO: Check collision
/* Apply plane effect */
if((w->plane->type == ELEMENT_PLANE_DAMAGE ||
w->plane->type == ELEMENT_PLANE_INVIS)
&& plane_collides(num16(camera->pos.x), num16(camera->pos.z),
w->plane->shape)) {
world_reset(w);
reset_frames = 2;
}
else if(w->plane->type == ELEMENT_PLANE_ARROW &&
camera->roll_quadrant != w->plane->data) {
world_reset(w);
reset_frames = 2;
}
w->plane = NULL;
}
if(w->text && w->depth > w->text->end)
@ -360,6 +491,8 @@ int update(void)
// Load incoming world elements
struct level const *level = levels[current_level];
if(!w->mirror && w->mirror_index < level->mirror_count
&& level->mirrors[w->mirror_index].begin < w->depth + num(64)) {
w->mirror = &level->mirrors[w->mirror_index++];
@ -376,34 +509,19 @@ int update(void)
// End of level
if(!w->mirror && !w->plane && !w->text && w->depth >= level->finish) {
gint_osmenu();
if(is_final_level(current_level)) {
// TODO: Main menu
return 1;
}
else {
world_level_transition(w);
load_level_number(current_level + 1);
}
}
return 0;
}
/* Basic basic level */
struct level lv1 = {
.name = "<Test>",
.finish = num(1024),
.mirror_count = 1,
.mirrors = (struct element_mirror[]){
{ .begin = 256, .end = 768 },
},
.plane_count = 2,
.planes = (struct element_plane[]){
{ .y = 384, .shape = 0b01010'10101'01010'10101'01010,
.type = ELEMENT_PLANE_DAMAGE, .data = 0 },
{ .y = 384+64, .shape = 0b11111'10001'10001'10001'11111,
.type = ELEMENT_PLANE_DAMAGE, .data = 0 },
},
.text_count = 1,
.texts = (struct element_text[]){
{ .begin = 32, .end = 256,
.str = "Red stuff bad!\nDon't touch the red stuff!", },
},
};
int main(void)
{
if(azur_init("Chaos Drop!", 3*VWIDTH, 3*VHEIGHT))
@ -415,16 +533,9 @@ int main(void)
camera_set_fov(camera, 80.0);
camera_update_angles(camera);
extern struct level level_1_1;
level = &level_1_1;
struct world w = {};
w.neon_position = 0;
w.depth = 0;
w.neon_period = 64;
w.mirror = NULL;
w.plane = NULL;
w.text = NULL;
load_level_number(0);
world_reset(&w);
world = &w;
init();

View File

@ -37,24 +37,36 @@ void camera_roll(struct camera *camera, float snap_factor)
float distance_wrap_up = (target_roll + 2*M_PI) - camera->roll;
float distance_wrap_down = (target_roll - 2*M_PI) - camera->roll;
float mv;
/* Snap with closest direction */
if(fabsf(distance_nowrap) < fabsf(distance_wrap_up)) {
if(fabsf(distance_nowrap) < fabsf(distance_wrap_down))
camera->roll += distance_nowrap * snap_factor;
mv = distance_nowrap * snap_factor;
else
camera->roll += distance_wrap_down * snap_factor;
mv = distance_wrap_down * snap_factor;
}
else {
if(fabsf(distance_wrap_down) < fabsf(distance_wrap_down))
camera->roll += distance_wrap_down * snap_factor;
mv = distance_wrap_down * snap_factor;
else
camera->roll += distance_wrap_up * snap_factor;
mv = distance_wrap_up * snap_factor;
}
camera->roll += mv;
if(camera->roll < 0)
camera->roll += 2 * M_PI;
else if(camera->roll > 2 * M_PI)
camera->roll -= 2 * M_PI;
/* Also rotate the player along with it */
float cos_mv, sin_mv;
sincosf(-mv, &sin_mv, &cos_mv);
num c = cos_mv, s = sin_mv;
num nx = camera->pos.x * c + camera->pos.z * s;
num nz = -camera->pos.x * s + camera->pos.z * c;
camera->pos.x = nx;
camera->pos.z = nz;
}
mat3 matrix_camera2world(struct camera const *camera)
@ -125,6 +137,15 @@ void camera_update_angles(struct camera *camera)
camera->up = c2w * vec3(0, 0, 1);
}
bool plane_collides(num16 x, num16 z, uint32_t shape)
{
int cx = ((x + num16(WORLD_SIZE / 2)) * num16(5.0 / WORLD_SIZE)).ifloor();
int cz = ((z + num16(WORLD_SIZE / 2)) * num16(5.0 / WORLD_SIZE)).ifloor();
return (unsigned)cx < 5 && (unsigned)cz < 5
&& ((shape >> (5*cz + 4-cx)) & 1);
}
using snum = num16;
using svec3 = vec<snum,3>;
svec3 svec3_of_vec3(vec3 v)
@ -140,7 +161,7 @@ enum object {
};
static int cast_ray(struct world const *world, svec3 const &origin,
svec3 const &rayDir, snum *t, num *collision_y)
svec3 const &rayDir, snum *t, num *collision_y, bool secondary)
{
snum tx_rx_rz, tz_rx_rz;
int hitx = 0, hitz = 0;
@ -189,24 +210,20 @@ static int cast_ray(struct world const *world, svec3 const &origin,
*collision_y = hit ? num(origin.y) + num(*t) * num(rayDir.y) : num(192);
/* Compute intersection with object plane */
if(__builtin_expect(world->plane &&
(*collision_y > world->plane->y - world->depth), 0)) {
if(__builtin_expect(
world->plane
&& (*collision_y > world->plane->y - world->depth)
&& (world->plane->type != ELEMENT_PLANE_INVIS || secondary),
0)) {
snum plane_y = snum(world->plane->y - world->depth);
snum plane_t = (plane_y - origin.y) / rayDir.y;
snum plane_x = origin.x + plane_t * rayDir.x;
snum plane_z = origin.z + plane_t * rayDir.z;
plane_x = (plane_x + snum(WORLD_SIZE / 2)) * snum(5.0 / WORLD_SIZE);
plane_z = (plane_z + snum(WORLD_SIZE / 2)) * snum(5.0 / WORLD_SIZE);
int cell_x = plane_x.ifloor();
int cell_z = plane_z.ifloor();
if((unsigned)cell_x < 5 && (unsigned)cell_z < 5) {
if((world->plane->shape >> (5*cell_z + 4-cell_x)) & 1) {
*t = plane_t;
*collision_y = world->plane->y - world->depth;
return OBJECT_PLANE;
}
if(plane_collides(plane_x, plane_z, world->plane->shape)) {
*t = plane_t;
*collision_y = world->plane->y - world->depth;
return OBJECT_PLANE;
}
}
@ -238,7 +255,7 @@ void render_fragment(struct camera const *camera, struct world const *world,
for(int x = 0; x < VWIDTH; x++) {
snum t;
num coll_y;
int obj = cast_ray(world, origin, rayDir, &t, &coll_y);
int obj = cast_ray(world, origin, rayDir, &t, &coll_y, false);
bool darken = false;
if(__builtin_expect(
@ -258,21 +275,24 @@ void render_fragment(struct camera const *camera, struct world const *world,
collision.x = origin.x + t * rayDir.x;
collision.y = snum(coll_y);
rayDir.x = -rayDir.x;
obj = cast_ray(world, collision, rayDir, &t, &coll_y);
obj = cast_ray(world, collision, rayDir, &t, &coll_y,true);
darken = true;
rayDir.x = -rayDir.x;
}
}
static uint16_t const colors[6] = {
static uint16_t const colors[] = {
RGB(3, 4, 8),
RGB(3, 4, 8), /* Left */
RGB(5, 6, 9), /* Top */
RGB(7, 8, 11), /* Right */
RGB(5, 6, 9), /* Bottom */
RGB(31, 0, 0), /* Object plane */
RGB(3, 4, 8), /* Left */
RGB(5, 6, 9), /* Top */
RGB(7, 8, 11), /* Right */
RGB(5, 6, 9), /* Bottom */
RGB(31, 0, 0), /* ELEMENT_PLANE_DAMAGE */
RGB(0, 31, 0), /* ELEMENT_PLANE_ARROW */
RGB(15, 0, 0), /* ELEMENT_PLANE_INVIS */
};
uint16_t color = colors[obj];
uint16_t color =
colors[obj + (obj == OBJECT_PLANE ? world->plane->type : 0)];
/* Don't show neons that are too far to avoid flickering */
if(coll_y < 64 && obj != OBJECT_PLANE) {