jtmm2/src/player.c

118 lines
3.9 KiB
C

#include <gint/display.h>
#include <stdbool.h>
#include "player.h"
#include "vec.h"
#include "conf.h"
#include "camera.h"
#include "input.h"
#include "collide.h"
/* TODO: Determine FRICTION and ACCELERATION from UPS. */
#define MAX_SPD (64 * PXS)
#define FRICTION (0.95)
#define ACCELERATION (int)(MAX_SPD * (1 - FRICTION))
#define GRAVITY PXS
#define JUMP_SPD (-128 * PXS)
#define GRACE_UNITS (int)(UPS / 5)
#define SGN(x) ((x > 0) ? (1) : ((x < 0) ? (-1) : (0)))
#define PLAYER_COLLIDE(pos) player_collide(player, pos, level, level->solid_layer)
void player_move(Player *player, const Level *level) {
/* TODO: Take into account player.origin. */
const int sgn_spd_x = SGN(player->spd.x);
const int sgn_spd_y = SGN(player->spd.y);
Vec destination;
vec_cpy(&destination, player->pos);
/* snap the player to the grid if they hit a wall */
destination.x += player->spd.x;
if (PLAYER_COLLIDE(destination)) {
destination.x = player->pos.x - player->pos.x % TILE_SIZE;
/* Move the player tile per tile until it enters an
* occuped tile. */
while (!PLAYER_COLLIDE(destination)) {
destination.x += TILE_SIZE * sgn_spd_x;
}
/* then, move it back one tile */
destination.x -= TILE_SIZE * sgn_spd_x;
player->spd.x = 0;
}
/* do the same for y */
destination.y += player->spd.y;
if (PLAYER_COLLIDE(destination)) {
destination.y = player->pos.y - player->pos.y % TILE_SIZE;
while (!PLAYER_COLLIDE(destination)) {
destination.y += TILE_SIZE * sgn_spd_y;
}
destination.y -= TILE_SIZE * sgn_spd_y;
if (sgn_spd_y > 0) { /* the player was falling */
player->grace = GRACE_UNITS;
}
player->spd.y = 0;
}
/* move the player to their new position */
vec_cpy(&player->pos, destination);
}
void player_step(Player *player, Input *input, const Level *level) {
/* Get directionnal input and assign it to move.x/move.y:
* i.e., if the player hold left and down move will have
* move.x = -1 and move.y = 1. */
Vec move = {
(input_is_down(input, K_RIGHT) - input_is_down(input, K_LEFT)),
(input_is_down(input, K_DOWN) - input_is_down(input, K_UP))
};
/* other keys */
bool kp_jump = input_is_pressed(input, K_JUMP);
int xacc = move.x * ACCELERATION; /* calculate horizontal acceleration */
player->spd.x *= FRICTION; /* apply horizontal friction */
player->spd.x += xacc; /* apply horizontal acceleration */
player->spd.y += GRAVITY; /* apply gravity */
/* Grace frames allow the player to jump a short
* time after leaving a platform. */
if (player->grace) {
player->grace -= 1;
if (kp_jump) {
/* If the player try to jump and can, prevent
* them to do it again and assign them the
* corresponding y speed. */
player->grace = 0;
player->spd.y = JUMP_SPD;
}
}
player_move(player, level); /* move the player according to their speed */
}
void player_draw(Player *player, Camera *camera) {
Vec tl; /* top left */
Vec br; /* bottom right */
/* The rest of this function calculates the player on screen
* position and draw it. */
vec_cpy(&tl, player->pos);
vec_sub(&tl, player->origin);
vec_cpy(&br, tl);
vec_div(&tl, VEC_PRECISION);
vec_div(&br, VEC_PRECISION);
vec_add(&br, player->vbox);
vec_sub(&tl, camera->offset);
vec_sub(&br, camera->offset);
vec_mul(&tl, SCALE);
vec_mul(&br, SCALE);
vec_add(&br, (Vec){ SCALE - 1, SCALE - 1 });
/* draw code here */
vec_drect(tl, br, C_BLACK);
}
void player_draw_debug(Player *player, uint step, const Level *level, uint layer_id) {
/* This debug function displays more or less usefull
* informations for debugging player movement. */
dprint(0, 0, C_BLACK, "x: %d", player->pos.x);
dprint(0, 10, C_BLACK, "y: %d", player->pos.y);
dprint(0, 20, C_BLACK, "vp: %d", VEC_PRECISION);
dprint(0, 30, C_BLACK, "st: %u", step);
dprint(0, 40, C_BLACK, "cx: %d", player->pos.x / TILE_SIZE);
dprint(0, 50, C_BLACK, "cy: %d", player->pos.y / TILE_SIZE);
dprint(0, 60, C_BLACK, "cl: %d", collide_point(player->pos, level, layer_id));
}