#include "player.h" #include "bullet.h" #include "conf.h" #include "draw.h" #include "hook.h" #include "input.h" #include "level.h" #include "tools.h" #include #include static int k_up, k_down, k_left, k_right, k_jump, k_jumpdown, k_hook, k_fire; static struct Player player; static void player_get_input(void); static void player_move(struct Vec2 spd); static void player_hook(void); static void player_hook_draw(void); static int player_update_frame(int timer); static struct Vec2 player_update_rem(); static int player_collide_pixel(int x, int y, tile_t id); static int player_collide(struct Vec2 pos, tile_t id, int h); extern bopti_image_t img_scarletR; extern bopti_image_t img_scarletL; extern bopti_image_t img_hooksel; struct Vec2 player_get_pos(void) { return player.pos; } void player_init(void) { player.pos = (struct Vec2){160, 100}; player.spd = (struct FVec2){0.0f, 0.0f}; player.rem = (struct FVec2){0.0f, 0.0f}; player.jumping = 1; player.airbreak = 1; player.hooking = 0; player.locked = 0; player.dir = 1; player.hook_pos = (struct Vec2){0, 0}; player.hook_near = hook_closest(&player.pos); } void player_update(void) { /* input and air movement */ player_get_input(); const int dir = k_right - k_left; if (player.jumping && (!k_jumpdown || player.spd.y > 0)) { player.airbreak = 1; } if (k_jump && !player.jumping) { player.spd.y = -PLAYER_JUMP; player.jumping = 1; player.airbreak = 0; } /* hooking */ player_hook(); if (!player.hooking) { player.spd.x *= (1 - PLAYER_FRIC); player.spd.x += dir * PLAYER_ACC; } player.spd.y += GRAVITY + GRAVITY * (player.airbreak && !player.hooking); /* applying movement */ player_move(player_update_rem()); if (player_collide((struct Vec2){player.pos.x, player.pos.y + 1}, 1, 0)) { player.jumping = 0; } /* shooting */ if (k_fire) { bullet_fire(player.pos.x, player.pos.y, (dir ? dir : player.dir) * 6); } } void player_hook(void) { player.hooking = 0; struct Vec2 player_middle = {player.pos.x + PLAYER_S / 2, player.pos.y + PLAYER_S / 2}; player.hook_near = hook_closest(&player_middle); if (k_hook) { /* get the nearest hook */ if (!player.locked) { player.hook_pos = player.hook_near; } const float dist = length(&player_middle, &player.hook_pos); /* if the nearest hook is in range */ if (dist < 100 && player_middle.y > player.hook_pos.y) { player.hooking = 1; player.locked = 1; player.jumping = 1; } if (player.hooking && dist) { /* determining hook force */ float dist_x = player.hook_pos.x - player_middle.x; float dist_y = player.hook_pos.y - player_middle.y; struct FVec2 force = {}; force.x = dist_x / dist; force.y = dist_y / dist; /* apply hook force */ player.spd.x += force.x; player.spd.y += force.y; /* cap speed */ if (abs(player.spd.x) > 16) { player.spd.x = 16 * sign(player.spd.x); } if (abs(player.spd.y) > 16) { player.spd.y = 16 * sign(player.spd.y); } } } else { /* unbind the player from the hook */ player.locked = 0; } } void player_draw(int timer) { const int frame = player_update_frame(timer); if (player.dir == 1) { dsubimage(player.pos.x - 5, player.pos.y - 5, &img_scarletR, frame * PLAYER_W, 0, PLAYER_W, PLAYER_H, 0); } else { dsubimage(player.pos.x - 5, player.pos.y - 5, &img_scarletL, frame * PLAYER_W, 0, PLAYER_W, PLAYER_H, 0); } /* draw_rectangle(player.pos.x, player.pos.y, PLAYER_S, PLAYER_S, * C_WHITE); */ player_hook_draw(); } int player_update_frame(int timer) { if (player.hooking) { return 5; } if (player.jumping && player.spd.y <= 0) { return 2; } if (player.jumping && player.spd.y > 0) { return 3; } if (abs(player.spd.x) > 0) { return (6 + (timer / 2) % 8); } return ((timer / 60) % 2); } void player_hook_draw(void) { struct Vec2 player_middle = {player.pos.x + PLAYER_S / 2, player.pos.y + PLAYER_S / 2}; if (length(&player_middle, &player.hook_near) < 100 && player_middle.y > player.hook_near.y) { dimage(player.hook_near.x - 5, player.hook_near.y - 5, &img_hooksel); }; if (player.hooking) { dline(player.pos.x + PLAYER_S / 2, player.pos.y + 7, player.hook_pos.x, player.hook_pos.y, C_RED); } } static void player_get_input(void) { k_up = input_down(K_UP); k_down = input_down(K_DOWN); k_left = input_down(K_LEFT); k_right = input_down(K_RIGHT); k_jump = input_pressed(K_A); k_jumpdown = input_down(K_A); k_hook = input_down(K_B); k_fire = input_pressed(K_X); } static struct Vec2 player_update_rem(void) { struct FVec2 spdrem = (struct FVec2){player.spd.x + player.rem.x, player.spd.y + player.rem.y}; struct Vec2 ispd = (struct Vec2){spdrem.x, spdrem.y}; player.rem.x = spdrem.x - (float)ispd.x; player.rem.y = spdrem.y - (float)ispd.y; return ispd; } static void player_move(struct Vec2 spd) { if (player_collide(player.pos, 1, 0)) { return; } /* OOB */ const struct Vec2 level_dim = level_get_dim(); if (spd.x) { const int sign_x = sign(spd.x); player.dir = sign_x; player.pos.x += spd.x; if (player.pos.x < 0) { player.pos.x = 1; } if (player.pos.x > level_dim.x * TILE_S) { player.pos.x = level_dim.x * TILE_S - 1; } if (player_collide(player.pos, 1, 0)) { player.spd.x = 0.0f; player.rem.x = 0.0f; while (player_collide(player.pos, 1, 0)) { player.pos.x -= sign_x; } } } if (spd.y) { const int sign_y = sign(spd.y); player.pos.y += spd.y; if (player.pos.y < 0) { player.pos.y = 1; } if (player.pos.y > level_dim.y * TILE_S) { player.pos.y = level_dim.y * TILE_S - 1; } if (player_collide(player.pos, 1, 0)) { player.spd.y = 0.0f; player.rem.y = 0.0f; while (player_collide(player.pos, 1, 0)) { player.pos.y -= sign_y; } } } } static int player_collide_pixel(int x, int y, tile_t id) { const tile_t tile = level_get_px(x, y); if (!tile) { return 0; } if (id == 1) { return (tile - 1) % TILESET_W < 8; } return tile == id; } static int player_collide(struct Vec2 pos, tile_t id, int h) { const struct Vec2 pos_tl = (struct Vec2){pos.x + h, pos.y + h}; const struct Vec2 pos_br = (struct Vec2){pos.x + PLAYER_S - h - 1, pos.y + PLAYER_S - h - 1}; const struct Vec2 middle = (struct Vec2){pos.x + PLAYER_S / 2, pos.y + PLAYER_S / 2}; if (player_collide_pixel(pos_tl.x, pos_tl.y, id) || player_collide_pixel(pos_br.x, pos_br.y, id) || player_collide_pixel(pos_tl.x, pos_br.y, id) || player_collide_pixel(pos_br.x, pos_tl.y, id) || player_collide_pixel(middle.x, pos_tl.y, id) || player_collide_pixel(middle.x, pos_br.y, id) || player_collide_pixel(pos_tl.x, middle.y, id) || player_collide_pixel(pos_br.x, middle.y, id)) { return 1; } return 0; }