#include "map.h" #include "level.h" #include "render.h" #include "geometry.h" #include "entities.h" #include "game.h" #include "anim.h" #include #include #include #include #include #include #include #include #include static bool getkey_global_shortcuts(key_event_t e) { if(usb_is_open() && e.key == KEY_OPTN && !e.shift && !e.alpha) { usb_fxlink_screenshot(true); return true; } return false; } extern level_t lv_demo; int OVERLAY_HITBOXES = 0; int main(void) { getkey_set_feature_function(getkey_global_shortcuts); __printf_enable_fp(); srand(0xc0ffee); prof_init(); usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL }; usb_open(interfaces, GINT_CALL_NULL); game_t game = { 0 }; map_t *m = &game.map; camera_t *c = &game.camera; game_load(&game, &lv_demo); //--- // Load random entities on the map //--- entity_movement_params_t emp_none = { .max_speed = fix(0), .propulsion = fix(0), }; entity_movement_params_t emp_player = { .max_speed = PLAYER_SPEED, .propulsion = fix(11), .dash_speed = fix(45), .dash_duration = fix(1) / 32, .dash_cooldown = fix(1) / 2, }; for(int i = 0; i < 10; i++) { entity_t *e = malloc(sizeof *e); if(!e) continue; int x=1, y=1; for(int i = 0; i < 1000; i++) { x = rand() % m->width; y = rand() % m->height; struct tile *t = map_tile(m, x, y); if(t && !t->solid) break; } e->movement.x = fix(x) + fix(1) / 2; e->movement.y = fix(y) + fix(1) / 2; e->movement.vx = 0; e->movement.vy = 0; e->movement.facing = rand() % 4; e->movement.dash = 0; e->movement.dash_facing = 0; e->movement_params = &emp_none; e->anim.frame = NULL; e->anim.elapsed = 0; if(rand() % 2) { e->hitbox = (shape_t){ SHAPE_RECT, .rect = { .l=-fix(1)/4, .r=fix(1)/4, .t=-fix(1)/4, .b=fix(1)/4 }, .color = C_GREEN, }; } else { e->hitbox = (shape_t){ SHAPE_CIRCLE, .circle = { .x=0, .y=0, .r=fix(1)/4 }, .color = C_GREEN, }; } game_add_entity(&game, e); } entity_t *player = game.entities[5]; game.player = player; player->hitbox.color = C_BLUE; player->movement_params = &emp_player; extern anim_frame_t anim_player_idle_left[]; extern anim_frame_t anim_player_idle_right[]; extern anim_frame_t anim_player_idle_up[]; extern anim_frame_t anim_player_idle_down[]; player->anim.frame = &anim_player_idle_down[0]; player->anim.elapsed = 0; //--- // Main loop //--- volatile int frame_tick = 1; int timer_id = timer_configure(TIMER_ANY, 1000000 / FRAME_RATE, GINT_CALL_SET(&frame_tick)); if(timer_id >= 0) timer_start(timer_id); uint32_t time_render = 0; bool stop = false; bool show_vars = false; while(!stop) { while(!frame_tick) sleep(); bool attack = false; /* Assume the frame is not late */ fixed_t dt = fix(1) / FRAME_RATE; time_render = prof_exec({ dclear(C_RGB(2,1,1)); render_game(&game); if(show_vars) { for(int i = 0; i < 396 * 224; i++) { gint_vram[i] = (gint_vram[i] & 0xf7de) >> 1; } uint16_t gray = C_RGB(16, 16, 16); dprint(3, 40, C_WHITE, "Max player speed: %g tiles/s", f2double(emp_player.max_speed)); dprint(15, 55, gray, "More with [X,0,T], less with [frac]"); dprint(3, 70, C_WHITE, "Propulsion: %g s^-1", f2double(emp_player.propulsion)); dprint(15, 85, gray, "More with [log], less with [F<>D]"); dprint(15, 100, C_WHITE, "(Equivalent friction: %g)", f2double(fix(1) - emp_player.propulsion / FRAME_RATE)); dprint(3, 115, C_WHITE, "Dash speed: %g tiles/s", f2double(emp_player.dash_speed)); dprint(15, 130, gray, "More with [ln], less with [(]"); dprint(3, 145, C_WHITE, "Dash duration: %g s", f2double(emp_player.dash_duration)); dprint(15, 160, gray, "More with [sin], less with [)]"); dprint(3, 175, C_WHITE, "Dash cooldown: %g s", f2double(emp_player.dash_cooldown)); dprint(15, 190, gray, "More with [cos], less with [,]"); } // dprint(1, 1, C_WHITE, "Frame: %.3j ms", time_render); dprint(1, 1, C_WHITE, "dash:%g", f2double(player->movement.dash)); dupdate(); }); game_update_animations(&game, dt); key_event_t ev; while((ev = pollevent()).type != KEYEV_NONE) { if(ev.type == KEYEV_UP) continue; if(ev.key == KEY_MENU) gint_osmenu(); if(ev.key == KEY_EXIT) stop = true; if(ev.key == KEY_F6 && usb_is_open()) usb_fxlink_screenshot(true); if(ev.key == KEY_F5) OVERLAY_HITBOXES ^= 1; /* Parameter adjustement */ if(ev.key == KEY_VARS) show_vars = !show_vars; if(ev.key == KEY_XOT) emp_player.max_speed += fix(1)/8; if(ev.key == KEY_FRAC) { emp_player.max_speed -= fix(1)/8; if(emp_player.max_speed < 0) emp_player.max_speed = 0; } if(ev.key == KEY_LOG) { emp_player.propulsion += fix(1) / 8; if(emp_player.propulsion > fix(FRAME_RATE)) emp_player.propulsion = fix(FRAME_RATE); } if(ev.key == KEY_FD) { emp_player.propulsion -= fix(1) / 8; if(emp_player.propulsion <= 0) emp_player.propulsion = 0; } if(ev.key == KEY_LN) emp_player.dash_speed += fix(1) / 2; if(ev.key == KEY_LEFTP) { emp_player.dash_speed -= fix(1) / 2; if(emp_player.dash_speed <= 0) emp_player.dash_speed = 0; } if(ev.key == KEY_SIN) emp_player.dash_duration += fix(1) / 64; if(ev.key == KEY_RIGHTP) { emp_player.dash_duration -= fix(1) / 64; if(emp_player.dash_duration <= 0) emp_player.dash_duration = 0; } if(ev.key == KEY_COS) emp_player.dash_cooldown += fix(1) / 8; if(ev.key == KEY_COMMA) { emp_player.dash_cooldown -= fix(1) / 8; if(emp_player.dash_cooldown <= 0) emp_player.dash_cooldown = 0; } if(ev.key == KEY_PLUS) camera_zoom(c, c->zoom + 1); if(ev.key == KEY_MINUS) camera_zoom(c, c->zoom - 1); if(ev.key == KEY_SHIFT) attack = true; } /* Camera movement */ fixed_t vx = CAMERA_SPEED_X; fixed_t vy = CAMERA_SPEED_Y; if(keydown(KEY_4) || keydown(KEY_7) || keydown(KEY_1)) camera_move(c, -fmul(dt, vx), 0); if(keydown(KEY_6) || keydown(KEY_9) || keydown(KEY_3)) camera_move(c, fmul(dt, vx), 0); if(keydown(KEY_8) || keydown(KEY_7) || keydown(KEY_9)) camera_move(c, 0, -fmul(dt, vy)); if(keydown(KEY_2) || keydown(KEY_1) || keydown(KEY_3)) camera_move(c, 0, fmul(dt, vy)); /* Player movement */ int dir = -1; if(keydown(KEY_UP)) dir = UP; if(keydown(KEY_DOWN)) dir = DOWN; if(keydown(KEY_LEFT)) dir = LEFT; if(keydown(KEY_RIGHT)) dir = RIGHT; if(keydown(KEY_F1)) { int dash_dir = (dir >= 0) ? dir : player->movement.facing; entity_dash(player, dash_dir); } entity_movement_t next = entity_move(player, dir, dt); shape_t player_hitbox = player->hitbox; shape_translate(&player_hitbox, (fpoint_t){ next.x, next.y }); if(player->movement.facing != next.facing) { anim_frame_t *dirs[4] = { anim_player_idle_up, anim_player_idle_right, anim_player_idle_down, anim_player_idle_left, }; player->anim.frame = dirs[next.facing]; } if(!game_shape_collides(&game, player, &player_hitbox)) { player->movement = next; } else { player->movement.facing = next.facing; player->movement.vx = fix(0); player->movement.vy = fix(0); player->movement.dash = next.dash; } /* Attack */ if(attack) { fixed_t reach2 = fmul(ATTACK_REACH, ATTACK_REACH); fixed_t angle_cos2 = fmul(ATTACK_ANGLE_COS, ATTACK_ANGLE_COS); fpoint_t dir = fdir(player->movement.facing); for(int i = 0; i < game.entity_count; i++) { entity_t *e = game.entities[i]; if(e == player) continue; /* Distance to player */ fixed_t dx = e->movement.x - player->movement.x; fixed_t dy = e->movement.y - player->movement.y; fixed_t dist2 = fmul(dx, dx) + fmul(dy, dy); if(dist2 > reach2) continue; /* Attack angle */ fixed_t dotp = fmul(dir.x, dx) + fmul(dir.y, dy); if(dotp < 0 || fmul(dotp, dotp) < fmul(dist2, angle_cos2)) continue; /* Inflict damage */ e->hitbox.color = C_RED; } } } timer_stop(timer_id); prof_quit(); usb_close(); return 1; }