#include "level.h" #include "generator.h" #include "render.h" #include "game.h" #include "util.h" #include "log.h" #include "menu.h" #include #include #include #include #include #include #include #include #include #include #ifdef LOG_USB_ENABLE #include #include /* Screenshot next frame */ bool volatile screenshot = false; /* Send next frame as video to fxlink */ bool volatile videocapture = false; GUNUSED static bool async_event_filter(key_event_t ev) { if(ev.key == KEY_7) { if(ev.type == KEYEV_DOWN) screenshot = true; return false; } if(ev.key == KEY_8) { if(ev.type == KEYEV_DOWN) videocapture = !videocapture; return false; } return true; } GUNUSED static void hook_prefrag(int id, void *fragment, int size) { if(!screenshot && !videocapture) return; /* Screenshot takes precedence */ char const *type = screenshot ? "image" : "video"; int pipe = usb_ff_bulk_output(); if(id == 0) { usb_fxlink_header_t h; usb_fxlink_image_t sh; int size = azrp_width * azrp_height * 2; usb_fxlink_fill_header(&h, "fxlink", type, size + sizeof sh); sh.width = htole32(azrp_width); sh.height = htole32(azrp_height); sh.pixel_format = htole32(USB_FXLINK_IMAGE_RGB565); usb_write_sync(pipe, &h, sizeof h, false); usb_write_sync(pipe, &sh, sizeof sh, false); } usb_write_sync(pipe, fragment, size, false); if(id == azrp_frag_count - 1) { usb_commit_sync(pipe); screenshot = false; } } GUNUSED static void hook_dupdate(void) { if(screenshot) { usb_fxlink_screenshot(true); screenshot = false; } else if(videocapture) { usb_fxlink_videocapture(false); } } #endif /* LOG_USB_ENABLE */ int play_level(int level_id) { struct game game; struct camera *camera = &game.camera; struct player *player = &game.player; game.level = level_create(level_id); game.t = 0.0; game.t_death = 0.0; game.debug.footer = false; game.debug.invincible = false; camera->set_fov(120.0); camera->screen_size = vec2(DWIDTH, DHEIGHT); player->z = 0.0; player->vz = 3.0; player->platform = 0; player->stance = player::Falling; player->blueanim = 0.0; player->jump_dir = 0; player->jump_t = 0.0; player->height = 1.0; player->vheight = 0.0; player->frame = 0.0; player->energy_percent = 0.0; /* Input buffering */ struct { int left, right, up, shift; } keybuffer = {}; /* FPS regulation setup */ int volatile need_frame = 1; int last_frame_us = 0; int timer = timer_configure(TIMER_ANY, 33000, GINT_CALL_SET(&need_frame)); timer_start(timer); camera_track(camera, player); log_printf("cylinder quasiradius: %s\n",str(space_cylinder_quasiradius())); log_printf("player: at %s\n", str(player->pos())); log_printf("camera: at %s, screen distance %s, platform %d, " "angle vector %s\n", str(camera->pos), str(camera->screen_distance()), camera->platform, str(camera->angle_vector)); if(level_id == 4) { player->energy_percent = 350.0; player->height = num(-10.0); game.t_death = num(10.0); level_update(&game.level, num(350.0), 0); } bool game_run = true; while(game_run) { while(!need_frame) sleep(); need_frame = 0; //======= Initial data updates =======// num dt = 1.0 / 30; game.t += dt; prof_t perf_frame = prof_make(); prof_enter_norec(perf_frame); level_update(&game.level, player->z, player->platform); camera_track(camera, player); struct platform *standing_on = game_platform_under_player(&game, &game.player); struct platform *hitting = game_block_hitting_player(&game, &game.player); //======= Rendering =======// prof_t perf_comp = prof_make(); render_game(game, &perf_comp); num floor = 0; if(game.debug.footer) { drect(0, DHEIGHT-20, DWIDTH-1, DHEIGHT-1, C_WHITE); 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); r61524_display(gint_vram, DHEIGHT-20, 20, R61524_DMA_WAIT); } //======= Input =======// key_event_t ev; while((ev = pollevent()).type != KEYEV_NONE) { if(ev.type == KEYEV_UP || ev.type == KEYEV_HOLD) continue; int key = ev.key; if(key == KEY_EXIT || key == KEY_MENU) game_run = false; if(key == KEY_OPTN) game.debug.footer = !game.debug.footer; #if 0 if(key == KEY_VARS) game.debug.invincible = !game.debug.invincible; #endif if(key == KEY_LEFT) keybuffer.left = 8; if(key == KEY_RIGHT) keybuffer.right = 8; if(key == KEY_UP) keybuffer.up = 8; if(key == KEY_SHIFT) keybuffer.shift = 8; } if(!game_run) break; int key = keybuffer.left ? KEY_LEFT : keybuffer.right ? KEY_RIGHT : keybuffer.up ? KEY_UP : keybuffer.shift ? KEY_SHIFT : 0; if(key && !player->airborne()) { player->stance = player::Jumping; player->jump_dir = (key == KEY_RIGHT) - (key == KEY_LEFT); player->jump_t = 0.0; player->jump_key = key; player->vheight = JUMP_THRUST; player->frame = 0.0; keybuffer.left = 0; keybuffer.right = 0; keybuffer.up = 0; keybuffer.shift = 0; } if(keybuffer.left > 0) keybuffer.left--; if(keybuffer.right > 0) keybuffer.right--; if(keybuffer.up > 0) keybuffer.up--; if(keybuffer.shift > 0) keybuffer.shift--; //======= Simulation =======// num vz = player->vz * (num(1) + player->energy_percent / 200); if(player->stance == player::Collided) { game.t_death += dt; } else { player->z += vz * dt; player->height += player->vheight * dt; } /* Apply gravity */ num gravity = 0.0; if(player->stance == player::Jumping) { gravity = HOVERING_GRAVITY; } else if(player->stance == player::Falling) { gravity = FALLING_GRAVITY; } player->vheight += gravity * dt; floor = game_height_at(&game, player->z, player->platform); if(player->airborne()) { player->jump_t += dt; /* Update the current platform number after rotation */ if(player->jump_t > TIME_ROTATION && player->jump_dir) { int p = player->platform + player->jump_dir; p = (p + PLATFORM_COUNT) % PLATFORM_COUNT; player->platform = p; player->jump_dir = 0; } /* Switch to falling if the jump key is released */ if(player->stance == player::Jumping && player->jump_t > TIME_JUMP_THRUST_MIN && !keydown(player->jump_key)) { player->stance = player::Falling; player->vheight = -1; } if((player->height >= floor - STEP_HEIGHT && player->height <= floor) || (game.debug.invincible && player->height < 0)) { player->stance = player::Running; player->jump_dir = 0; player->jump_t = 0.0; player->height = floor; player->vheight = 0; player->jump_key = 0; player->frame = 0.0; if(game.debug.invincible && player->height < 0) player->height = 0; } } else if(floor < player->height && player->stance != player::Collided) { 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) || (hitting != NULL); /* Death condition #2/2: below the kill plane */ bool death_2 = (player->height < LEVEL_RADIUS - KILL_PLANE_RADIUS); if(!game.debug.invincible && (death_1 || death_2)) { if(player->stance != player::Collided) shader_ptrace_reset(); player->stance = player::Collided; } else if(standing_on && standing_on->type == PLATFORM_BLUE) { player->energy_percent += dt * vz * 5.5; } else if(standing_on && standing_on->type == PLATFORM_RED) { standing_on->falling = true; } /* Blue platform animation progress */ if(standing_on && standing_on->type == PLATFORM_BLUE) { player->blueanim += dt; if(player->blueanim >= num(2.0)) player->blueanim -= num(2.0); } else if(player->blueanim > 0) { if(player->blueanim < num(2.0)) player->blueanim = num(2.0); player->blueanim += dt; if(player->blueanim >= num(2.5)) player->blueanim = 0; } for(auto &p: game.level.platform_buffer) { if(p.type == PLATFORM_RED && p.falling) { p.height -= PLATFORM_FALL_SPEED * dt; if(standing_on == &p) player->height = p.height; } } if(game.t_death >= TIME_DEATH_ANIMATION && keybuffer.shift) break; game_advance(&game); prof_leave_norec(perf_frame); last_frame_us = prof_time(perf_frame); } timer_stop(timer); return 0; } int main(void) { int level; __printf_enable_fp(); prof_init(); render_init(); space_init(); extern font_t font_boson; dfont(&font_boson); azrp_config_scale(1); #if LOG_USB_ENABLE dclear(C_WHITE); dtext_opt(DWIDTH/2, DHEIGHT/2, C_BLACK, C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, "Waiting for USB log...", -1); dupdate(); log_init(true); azrp_hook_set_prefrag(hook_prefrag); dupdate_set_hook(GINT_CALL(hook_dupdate)); keydev_set_async_filter(keydev_std(), async_event_filter); #endif while (1) { if (menu_title() != 0) { gint_osmenu(); continue; } while (menu_select_level(&level) >= 0) { play_level(level + 1); } } return 0; }