mirror of https://git.sr.ht/~kikoodx/momento
149 lines
3.8 KiB
C
149 lines
3.8 KiB
C
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
/* Copyright (C) 2021 KikooDX */
|
|
|
|
#include "conf.h"
|
|
#include "level.h"
|
|
#include "player.h"
|
|
#include "tiles.h"
|
|
#include <gint/keyboard.h>
|
|
|
|
static void player_move(struct Player *player, int x, int y);
|
|
|
|
extern struct Level level;
|
|
|
|
/* return 1 if exit reached, -1 on death/reset and 0 otherwise */
|
|
int player_update(struct Player *player)
|
|
{
|
|
Tile collisions[COLLIDE_POINTS];
|
|
const int on_ground =
|
|
player_collide_solid(player->x, player->y + 1);
|
|
|
|
/* process input */
|
|
const int dir_x = keydown(KEY_RIGHT) - keydown(KEY_LEFT);
|
|
/* const int dir_y = keydown(KEY_DOWN) - keydown(KEY_UP); */
|
|
const int k_jump = keydown(KEY_SHIFT);
|
|
const int kp_jump = k_jump && (!player->k_jump_previous);
|
|
player->k_jump_previous = k_jump;
|
|
|
|
/* horizontal friction/acceleration */
|
|
player->spd_x *= (on_ground) ? (1 - FRC_GND) : (1 - FRC_AIR);
|
|
player->spd_x += dir_x * ((on_ground) ? (ACC_GND) : (ACC_AIR));
|
|
|
|
/* air state */
|
|
/* enter AirRising state when player jump */
|
|
switch (player->air_state) {
|
|
case AirRising:
|
|
if (!k_jump)
|
|
player->air_state = AirBreaking;
|
|
if (player->spd_y > 0)
|
|
player->air_state = AirNeutral;
|
|
break;
|
|
case AirBreaking:
|
|
if (player->spd_y > 0)
|
|
player->air_state = AirNeutral;
|
|
break;
|
|
case AirNeutral:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* vertical air resistance and gravity */
|
|
player->spd_y *= 1 - AIR_RES;
|
|
if (!on_ground) {
|
|
/* smooth jump apex */
|
|
const float abs_spd_y = (player->spd_y > 0.0)
|
|
? (player->spd_y)
|
|
: (-player->spd_y);
|
|
float factor = 1.0;
|
|
if (abs_spd_y < 1.0)
|
|
factor =
|
|
0.5 + 0.5 * (player->spd_y * player->spd_y);
|
|
if (player->air_state == AirBreaking)
|
|
factor *= AIR_BREAKING_FACTOR;
|
|
player->spd_y += GRAVITY * factor;
|
|
}
|
|
|
|
/* input buffer */
|
|
if (kp_jump)
|
|
player->jump_buffer = JUMP_BUFFER;
|
|
else if (player->jump_buffer)
|
|
player->jump_buffer -= 1;
|
|
/* grace frames and jumps refill */
|
|
if (on_ground) {
|
|
player->jump_grace = JUMP_GRACE;
|
|
player->jumps_left = AIR_JUMPS;
|
|
} else if (player->jump_grace)
|
|
player->jump_grace -= 1;
|
|
|
|
/* jump (youhou) */
|
|
if (k_jump && player->jump_buffer &&
|
|
(player->jump_grace || player->jumps_left)) {
|
|
/* ground jump */
|
|
if (player->jump_grace) {
|
|
player->jump_grace = 0;
|
|
player->spd_y = JUMP_SPD;
|
|
}
|
|
/* air jump (burst) */
|
|
else {
|
|
player->jumps_left -= 1;
|
|
player->spd_y = AIR_JMP_SPD;
|
|
}
|
|
player->air_state = AirRising;
|
|
player->jump_buffer = 0;
|
|
}
|
|
|
|
/* speed reminder */
|
|
const float spd_n_rem_x = player->spd_x + player->rem_x;
|
|
const int spd_x = (int)spd_n_rem_x;
|
|
player->rem_x = spd_n_rem_x - spd_x;
|
|
const float spd_n_rem_y = player->spd_y + player->rem_y;
|
|
const int spd_y = (int)spd_n_rem_y;
|
|
player->rem_y = spd_n_rem_y - spd_y;
|
|
|
|
/* move the player */
|
|
player_move(player, 0, spd_y);
|
|
player_move(player, spd_x, 0);
|
|
|
|
/* get gold ($$$) */
|
|
level.gold -= player_collide_sub(player->x, player->y,
|
|
TILE_GOLD, TILE_VOID);
|
|
|
|
/* unlock exit */
|
|
if (level.exit_locked &&
|
|
player_collide_sub(player->x, player->y, TILE_SWITCH,
|
|
TILE_VOID))
|
|
level.exit_locked = 0;
|
|
|
|
/* check for death and exit */
|
|
if (!level.exit_locked &&
|
|
player_collide_tile(collisions, player->x, player->y,
|
|
TILE_EXIT, 1))
|
|
return 1;
|
|
if (player_collide_tile(collisions, player->x, player->y,
|
|
TILE_LETAL, 0))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static void player_move(struct Player *player, int x, int y)
|
|
{
|
|
const int sign_x = 1 * (x > 0) - 1 * (x < 0);
|
|
const int sign_y = 1 * (y > 0) - 1 * (y < 0);
|
|
player->x += x;
|
|
player->y += y;
|
|
if (player_collide_solid(player->x, player->y)) {
|
|
if (sign_x) {
|
|
player->spd_x = 0.0;
|
|
player->rem_x = 0.0;
|
|
}
|
|
if (sign_y) {
|
|
player->spd_y = 0.0;
|
|
player->rem_y = 0.0;
|
|
}
|
|
do {
|
|
player->x -= sign_x;
|
|
player->y -= sign_y;
|
|
} while (player_collide_solid(player->x, player->y));
|
|
}
|
|
}
|