/* 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 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)); } }