This repository has been archived on 2022-01-13. You can view files and clone it, but cannot push or open issues or pull requests.
jtmm2-old/src/player.c

215 lines
6.9 KiB
C

#include <gint/display.h>
#include <stdbool.h>
#include "player.h"
#include "player_vars.h"
#include "player_modifiers.h"
#include "vec.h"
#include "conf.h"
#include "camera.h"
#include "input.h"
#include "collide.h"
#include "tiles.h"
/* TODO: Determine FRICTION and ACCELERATION from UPS. */
#define MAX_SPD (128 * PXS)
#define FRICTION 0.01
#define ACCELERATION (int)(MAX_SPD * FRICTION)
#define GRAVITY_SCALAR 16
#define GRAVITY (PXS / GRAVITY_SCALAR)
#define FAST_FALL_FACTOR 3
#define JUMP_SPD (-128 * PXS)
#define GRACE_UNITS (int)(UPS / 10)
#define EARLY_UNITS (int)(UPS / 5)
#define H_CLIP_MARGIN (TILE_SIZE / 4)
#define V_CLIP_MARGIN (TILE_SIZE / 3)
#define SGN(x) (((x) > 0) ? (1) : (((x) < 0) ? (-1) : (0)))
#define PLAYER_COLLIDE_SOLID(pos) (player_collide_or(player, pos, level) & F_SOLID)
void player_init(Player *player, const Level *level) {
vec_cpy(&player->pos, level->start_pos);
player->spd.x = 0;
player->spd.y = 0;
player->hbox.x = TILE_SIZE - 1;
player->hbox.y = TILE_SIZE - 1;
player->vbox.x = 7;
player->vbox.y = 7;
player->origin.x = 0;
player->origin.y = 0;
player->grace = 0;
player->jump_held = false;
player->dead = false;
}
void player_set_vars(Player *player, const Level *level) {
player->vars.friction = FRICTION;
player->vars.acceleration = ACCELERATION;
player->vars.jump_spd = JUMP_SPD;
player->vars.gravity = GRAVITY;
player->vars.gravity_scalar = GRAVITY_SCALAR;
/* collide on all sides */
Vec pos = { player->pos.x - 1, player->pos.y };
Tile_flags flags_left;
Tile_flags flags_right;
Tile_flags flags_floor;
Tile_flags flags_ceil;
flags_left = player_collide_or(player, pos, level);
pos.x += 2;
flags_right = player_collide_or(player, pos, level);
pos.x -= 1;
pos.y += 1;
flags_floor = player_collide_or(player, pos, level);
pos.y -= 2;
flags_ceil = player_collide_or(player, pos, level);
uint8_t side_flags[sizeof(Tile_flags) * 8];
/* make sum for each flag */
for (uint i = 0; i < sizeof(Tile_flags) * 8; i++) {
Tile_flags cur_flag = 1 << i;
side_flags[i] = 0;
if (flags_left & cur_flag)
side_flags[i] |= D_LEFT;
if (flags_right & cur_flag)
side_flags[i] |= D_RIGHT;
if (flags_floor & cur_flag)
side_flags[i] |= D_FLOOR;
if (flags_ceil & cur_flag)
side_flags[i] |= D_CEIL;
}
/* apply modifiers */
player_mod_water(player, side_flags[I_WATER]);
player_mod_ice(player, side_flags[I_ICE]);
player_mod_glue(player, side_flags[I_GLUE]);
/* fix wrong values */
if (player->vars.friction > 1)
player->vars.friction = 1;
}
void player_move(Player *player, const Level *level) {
/* TODO: Take into account player's hitbox */
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_SOLID(destination)) {
/* Used for clipping and positionning. */
int offset = destination.y % TILE_SIZE;
/* If player is rising or offset is too high then don't clip. */
if (player->spd.y < 0 || offset > H_CLIP_MARGIN) {
offset = 0;
}
destination.y -= offset;
if (PLAYER_COLLIDE_SOLID(destination))
{
destination.y += offset;
destination.x = player->pos.x - player->pos.x % TILE_SIZE;
/* Move the player tile per tile until it enters an
* occupied tile. */
while (!PLAYER_COLLIDE_SOLID(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_SOLID(destination)) {
destination.y = player->pos.y - player->pos.y % TILE_SIZE;
while (!PLAYER_COLLIDE_SOLID(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, uint step) {
player_set_vars(player, 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_DOWN(K_RIGHT) - INPUT_DOWN(K_LEFT)),
(INPUT_DOWN(K_DOWN) - INPUT_DOWN(K_UP))
};
/* other keys */
bool k_jump = INPUT_DOWN(K_JUMP);
int xacc = move.x * player->vars.acceleration; /* calculate horizontal acceleration */
player->spd.x *= 1 - player->vars.friction; /* apply horizontal friction */
player->spd.x += xacc; /* apply horizontal acceleration */
/* apply gravity */
if (player->spd.y < 0 && !player->jump_held) {
/* The player is rising and let go the jump key,
* accelerate gravity until they reach 0. */
player->spd.y += player->vars.gravity * FAST_FALL_FACTOR *
player->vars.gravity_scalar;
} else {
player->spd.y += player->vars.gravity * player->vars.gravity_scalar;
}
/* Grace frames allow the player to jump a short
* time after leaving a platform. */
if (player->grace) {
player->grace -= 1;
if (k_jump && (INPUT_LAST_PRESS(K_JUMP) <= EARLY_UNITS)) {
/* 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 = player->vars.jump_spd;
player->jump_held = true;
input->last_press[K_JUMP] = 0;
}
}
/* See if the player is still holding their jump button, this is
* usefull for jump height and gravity manipulation. */
if (player->jump_held && !k_jump) {
player->jump_held = false;
}
player_move(player, level); /* move the player according to their speed */
/* player death */
if (player_collide_or(player, player->pos, level) & F_SPIKY) {
player->dead = true;
}
}
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_add(&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) {
/* 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));
}