mirror of https://git.sr.ht/~kikoodx/momento
182 lines
4.5 KiB
C
182 lines
4.5 KiB
C
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
/* Copyright (C) 2021 KikooDX */
|
|
|
|
#include "conf.h"
|
|
#include "input.h"
|
|
#include "level.h"
|
|
#include "particles.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 on pause, return 1 if exit reached, 2 on death/reset and 0
|
|
* otherwise */
|
|
int
|
|
player_update(struct Player *player, struct Input input)
|
|
{
|
|
Tile collisions[COLLIDE_POINTS];
|
|
const int on_ground = player_collide_solid(player->x, player->y + 1);
|
|
|
|
int jumped = 0;
|
|
const int previous_flip_h = player->anim.flip_h;
|
|
|
|
/* process input */
|
|
const int k_left = input.keystates[K_LEFT] != KS_UP;
|
|
const int k_right = input.keystates[K_RIGHT] != KS_UP;
|
|
const int dir_x = k_right - k_left;
|
|
const int k_jump = input.keystates[K_A] != KS_UP;
|
|
const int kp_jump = input.keystates[K_A] == KS_PRESS;
|
|
|
|
/* pause the game? */
|
|
if (input.keystates[K_START] == KS_PRESS)
|
|
return -1;
|
|
|
|
/* 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)) {
|
|
jumped = 1;
|
|
/* 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;
|
|
/* burst boost */
|
|
player->spd_x += (float)dir_x * BURST_BOOST;
|
|
}
|
|
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, MARGIN_GOLD);
|
|
|
|
/* unlock exit */
|
|
if (level.exit_locked &&
|
|
player_collide_sub(player->x, player->y, TILE_SWITCH, TILE_VOID,
|
|
MARGIN_SWITCH))
|
|
level.exit_locked = 0;
|
|
|
|
/* check for death and exit */
|
|
if (!level.exit_locked &&
|
|
player_collide_tile(collisions, player->x, player->y, TILE_EXIT, 0,
|
|
1)) {
|
|
return 1;
|
|
}
|
|
if (player_collide_tile(collisions, player->x, player->y, TILE_LETAL,
|
|
MARGIN_LETAL, 1)) {
|
|
return 2;
|
|
}
|
|
|
|
/* update animation */
|
|
if (jumped)
|
|
player->anim = player->jump_anim;
|
|
|
|
particle_update(&player->anim);
|
|
|
|
if (player->anim.life == 0) {
|
|
if (dir_x != 0)
|
|
player->anim = player->walk_anim;
|
|
else
|
|
player->anim = player->idle_anim;
|
|
}
|
|
|
|
/* horizontal flip */
|
|
player->anim.flip_h = previous_flip_h;
|
|
if (dir_x)
|
|
player->anim.flip_h = dir_x == -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));
|
|
}
|
|
}
|