interference/src/player.c

309 lines
6.8 KiB
C

#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 <math.h>
#include <stdlib.h>
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;
}