momento/src/player/update.c

182 lines
4.5 KiB
C
Raw Normal View History

2021-03-30 01:42:11 +02:00
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright (C) 2021 KikooDX */
2021-03-30 22:57:49 +02:00
#include "conf.h"
#include "input.h"
2021-04-04 17:33:17 +02:00
#include "level.h"
2021-04-21 17:51:59 +02:00
#include "particles.h"
2021-03-30 01:42:11 +02:00
#include "player.h"
2021-03-30 22:57:49 +02:00
#include "tiles.h"
2021-03-30 01:42:11 +02:00
#include <gint/keyboard.h>
2021-03-30 22:57:49 +02:00
static void player_move(struct Player *player, int x, int y);
2021-04-04 17:33:17 +02:00
extern struct Level level;
2021-04-21 17:51:59 +02:00
/* 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)
2021-03-30 01:42:11 +02:00
{
2021-04-01 01:39:45 +02:00
Tile collisions[COLLIDE_POINTS];
const int on_ground = player_collide_solid(player->x, player->y + 1);
2021-03-30 22:57:49 +02:00
2021-04-21 17:51:59 +02:00
int jumped = 0;
const int previous_flip_h = player->anim.flip_h;
2021-03-30 01:42:11 +02:00
/* 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;
2021-03-30 22:57:49 +02:00
/* pause the game? */
if (input.keystates[K_START] == KS_PRESS)
return -1;
2021-03-30 22:57:49 +02:00
/* 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;
}
2021-03-30 22:57:49 +02:00
/* vertical air resistance and gravity */
player->spd_y *= 1 - AIR_RES;
if (!on_ground) {
2021-03-30 22:57:49 +02:00
/* smooth jump apex */
const float abs_spd_y =
(player->spd_y > 0.0) ? (player->spd_y) : (-player->spd_y);
2021-03-30 22:57:49 +02:00
float factor = 1.0;
2021-03-31 15:47:51 +02:00
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;
2021-03-30 22:57:49 +02:00
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)) {
2021-04-21 17:51:59 +02:00
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;
2021-04-02 01:09:23 +02:00
}
2021-03-30 22:57:49 +02:00
/* 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);
2021-04-01 01:39:45 +02:00
2021-04-04 17:33:17 +02:00
/* get gold ($$$) */
level.gold -= player_collide_sub(player->x, player->y, TILE_GOLD,
TILE_VOID, MARGIN_GOLD);
2021-04-04 17:33:17 +02:00
/* unlock exit */
if (level.exit_locked &&
player_collide_sub(player->x, player->y, TILE_SWITCH, TILE_VOID,
MARGIN_SWITCH))
2021-04-04 17:33:17 +02:00
level.exit_locked = 0;
2021-04-01 01:39:45 +02:00
/* check for death and exit */
2021-04-04 17:33:17 +02:00
if (!level.exit_locked &&
player_collide_tile(collisions, player->x, player->y, TILE_EXIT, 0,
1)) {
2021-04-01 01:39:45 +02:00
return 1;
}
if (player_collide_tile(collisions, player->x, player->y, TILE_LETAL,
MARGIN_LETAL, 1)) {
return 2;
}
2021-04-21 17:51:59 +02:00
/* 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;
2021-04-01 01:39:45 +02:00
return 0;
2021-03-30 22:57:49 +02:00
}
static void
player_move(struct Player *player, int x, int y)
2021-03-30 22:57:49 +02:00
{
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)) {
2021-03-31 15:47:51 +02:00
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;
}
2021-03-30 22:57:49 +02:00
do {
player->x -= sign_x;
player->y -= sign_y;
} while (player_collide_solid(player->x, player->y));
}
2021-03-30 01:42:11 +02:00
}