diff --git a/CMakeLists.txt b/CMakeLists.txt index 52f44d5..9201ff1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/assets/1_1.txt b/assets/1_1.txt index 4f5994d..8e82638 100644 --- a/assets/1_1.txt +++ b/assets/1_1.txt @@ -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." diff --git a/assets/1_2.txt b/assets/1_2.txt new file mode 100644 index 0000000..2be9d9a --- /dev/null +++ b/assets/1_2.txt @@ -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!" diff --git a/assets/1_3.txt b/assets/1_3.txt new file mode 100644 index 0000000..8ab4e15 --- /dev/null +++ b/assets/1_3.txt @@ -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. *" diff --git a/assets/2_1.txt b/assets/2_1.txt new file mode 100644 index 0000000..c865803 --- /dev/null +++ b/assets/2_1.txt @@ -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?" diff --git a/converters.py b/converters.py index 404b52d..1a84a22 100644 --- a/converters.py +++ b/converters.py @@ -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": diff --git a/src/chaos-drop.h b/src/chaos-drop.h index 1b497c8..5976cdb 100644 --- a/src/chaos-drop.h +++ b/src/chaos-drop.h @@ -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 ===// diff --git a/src/main.cc b/src/main.cc index 749f0cd..6de4191 100644 --- a/src/main.cc +++ b/src/main.cc @@ -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 = "", + .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 = "", - .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(); diff --git a/src/raytracing.cc b/src/raytracing.cc index fb8b387..295d0d6 100644 --- a/src/raytracing.cc +++ b/src/raytracing.cc @@ -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; 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) {