397 lines
14 KiB
C
397 lines
14 KiB
C
#include "header.h"
|
|
#include <raymath.h>
|
|
|
|
#define PLAYER_ACCEL 2750
|
|
#define AIR_ACCEL 900
|
|
#define RUN_INIT_SPD 250
|
|
#define JUMP_SPD 350
|
|
#define GRAV 1200
|
|
#define DASH_SPD 600
|
|
#define DEFAULT_JUMP_COUNT 1
|
|
|
|
static bool allow_move = true;
|
|
static bool allow_friction = true;
|
|
static bool on_ground = true;
|
|
static bool short_hop = false;
|
|
|
|
static unsigned int jumps = DEFAULT_JUMP_COUNT;
|
|
static unsigned int dash_count = 1;
|
|
|
|
static unsigned int frame_counter = 0;
|
|
static unsigned int afterimage_fcounter = 10;
|
|
|
|
static Color afterimage_c = (Color){0,0,0,255};
|
|
|
|
static int run_dir = 1;
|
|
static Vector2 dash_vec = (Vector2){0.0, 0.0};
|
|
|
|
const unsigned int run_start_frames = 10;
|
|
const unsigned int jump_squat_frames = 4;
|
|
const unsigned int land_lag_frames = 2;
|
|
const unsigned int dash_time_frames = 8;
|
|
const unsigned int afterimage_frames = 10;
|
|
|
|
static enum PLAYER_STATE state_buffer = IDLE;
|
|
unsigned int PLAYER_SIZE = 30;
|
|
|
|
extern struct target_obj_node *target_HEAD;
|
|
|
|
// The player FSM
|
|
void player_input_check(struct player_obj *player){
|
|
Vector2 accel = (Vector2){
|
|
.x = 0,
|
|
.y = 0
|
|
};
|
|
switch(player->state){
|
|
case IDLE:
|
|
if (IsKeyDown(LEFT) || IsKeyDown(RIGHT)){
|
|
player->state = RUN_START;
|
|
player->kinematic.velocity.x = (IsKeyDown(KEY_RIGHT)-IsKeyDown(KEY_LEFT)) * RUN_INIT_SPD;
|
|
}
|
|
break;
|
|
case RUN_START:
|
|
if (player->kinematic.velocity.x == 0){
|
|
frame_counter = 0;
|
|
player->state = IDLE;
|
|
}else{run_dir = sign(player->kinematic.velocity.x);
|
|
// Run Opposite Direction
|
|
if ((IsKeyPressed(LEFT) && run_dir == 1) || (IsKeyPressed(RIGHT) && run_dir == -1)){
|
|
frame_counter = 0;
|
|
player->kinematic.velocity.x = -run_dir * RUN_INIT_SPD;
|
|
}else{
|
|
// Complete the run startup
|
|
if(frame_counter<run_start_frames){
|
|
++frame_counter;
|
|
if (IsKeyDown(LEFT) || IsKeyDown(RIGHT)){
|
|
player->kinematic.velocity.x = run_dir * RUN_INIT_SPD;
|
|
}
|
|
}else{
|
|
frame_counter = 0;
|
|
player->state = RUNNING;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
case RUNNING:
|
|
run_dir = sign(player->kinematic.velocity.x);
|
|
if ((IsKeyPressed(LEFT) && run_dir == 1) || (IsKeyPressed(RIGHT) && run_dir == -1)){
|
|
player->state = RUN_END;
|
|
}else{
|
|
if (!IsKeyDown(LEFT) && !IsKeyDown(RIGHT)){
|
|
player->state = RUN_END;
|
|
}else{
|
|
accel.x = PLAYER_ACCEL*(IsKeyDown(KEY_RIGHT)-IsKeyDown(KEY_LEFT));
|
|
}
|
|
}
|
|
set_squish_target_offset(player->image, 0, 0);
|
|
set_squish_target_offset(player->image, 2, 0);
|
|
if (player->kinematic.velocity.x == 0){
|
|
if (place_meeting(&player->kinematic, (Vector2){1,0})){
|
|
player->kinematic.dim_reduction[0] = PLAYER_SIZE / 2;
|
|
//set_squish_target_offset(player->image, 2, 15);
|
|
}else if (place_meeting(&player->kinematic, (Vector2){1,0})){
|
|
player->kinematic.dim_reduction[2] = PLAYER_SIZE / 2;
|
|
//set_squish_target_offset(player->image, 2, 15);
|
|
}
|
|
player->state = IDLE;
|
|
}
|
|
break;
|
|
case RUN_END:
|
|
if(player->kinematic.velocity.x < 10 && player->kinematic.velocity.x > -10){
|
|
set_squish_target_offset(player->image, 2, 0);
|
|
player->kinematic.dim_reduction[2] = 0;
|
|
set_squish_target_offset(player->image, 0, 0);
|
|
player->kinematic.dim_reduction[0] = 0;
|
|
if(IsKeyDown(LEFT) || IsKeyDown(RIGHT)){
|
|
player->state = RUNNING;
|
|
}else{
|
|
player->state = IDLE;
|
|
}
|
|
}else{
|
|
//Skidding
|
|
if (player->kinematic.velocity.x > 0){
|
|
if (!IsKeyDown(RIGHT)){
|
|
set_squish_target_offset(player->image, 0, 15);
|
|
player->kinematic.dim_reduction[0] = 10;
|
|
}else{
|
|
set_squish_target_offset(player->image, 0, 0);
|
|
player->kinematic.dim_reduction[0] = 0;
|
|
}
|
|
}else{
|
|
if (!IsKeyDown(LEFT)){
|
|
set_squish_target_offset(player->image, 2, 15);
|
|
player->kinematic.dim_reduction[2] = 10;
|
|
}else{
|
|
set_squish_target_offset(player->image, 2, 0);
|
|
player->kinematic.dim_reduction[2] = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case JUMP_SQUAT:
|
|
player->kinematic.set_dim_reduction[1] = 10;
|
|
set_squish_target_offset(player->image, 1, 20);
|
|
if(frame_counter<jump_squat_frames){
|
|
++frame_counter;
|
|
if (short_hop != true && !IsKeyDown(JUMP)){
|
|
short_hop = true;
|
|
}
|
|
}
|
|
else{
|
|
frame_counter = 0;
|
|
if (short_hop == true)
|
|
player->kinematic.velocity.y = -JUMP_SPD * 0.6;
|
|
else
|
|
player->kinematic.velocity.y = -JUMP_SPD;
|
|
player->state = JUMPING;
|
|
short_hop = false;
|
|
}
|
|
break;
|
|
case JUMPING:
|
|
accel.x = AIR_ACCEL*(IsKeyDown(KEY_RIGHT)-IsKeyDown(KEY_LEFT));
|
|
if (player->kinematic.velocity.y >= 0){
|
|
player->state = FALLING;
|
|
}
|
|
break;
|
|
case FALLING:
|
|
accel.x = AIR_ACCEL*(IsKeyDown(KEY_RIGHT)-IsKeyDown(KEY_LEFT));
|
|
if (on_ground){
|
|
frame_counter = 0;
|
|
player->state = LANDING;
|
|
state_buffer = IDLE;
|
|
}
|
|
break;
|
|
case LANDING:
|
|
dash_count = 1;
|
|
if (frame_counter == 0){
|
|
player->kinematic.dim_reduction[3] = 0;
|
|
player->kinematic.set_dim_reduction[3] = 0;
|
|
player->kinematic.dim_reduction[1] = PLAYER_SIZE;
|
|
player->kinematic.set_dim_reduction[1] = 0;
|
|
set_squish_target_offset(player->image, 1, 0);
|
|
}
|
|
if(frame_counter<land_lag_frames){
|
|
++frame_counter;
|
|
if (IsKeyDown(JUMP))
|
|
state_buffer = JUMP_SQUAT;
|
|
}
|
|
else{
|
|
frame_counter = 0;
|
|
jumps = DEFAULT_JUMP_COUNT;
|
|
if (state_buffer == JUMP_SQUAT){
|
|
player->state = state_buffer;
|
|
--jumps;
|
|
}else if (IsKeyDown(LEFT) || IsKeyDown(RIGHT))
|
|
player->state = RUNNING;
|
|
else
|
|
player->state = IDLE;
|
|
}
|
|
break;
|
|
case DASHING:
|
|
player->kinematic.velocity.x = dash_vec.x;
|
|
player->kinematic.velocity.y = dash_vec.y;
|
|
/*if (player->kinematic.velocity.x > 0)
|
|
player->kinematic.dim_reduction[2] = -32;
|
|
else
|
|
player->kinematic.dim_reduction[0] = -32;*/
|
|
++frame_counter;
|
|
|
|
|
|
struct target_obj_node *target_current = target_HEAD;
|
|
while(target_current){
|
|
if (collide_target(&player->kinematic, target_current->obj)==true){
|
|
dash_count = 1;
|
|
}
|
|
target_current = target_current->next;
|
|
}
|
|
|
|
if (frame_counter > dash_time_frames){
|
|
player->kinematic.velocity.x *= 0.8;
|
|
player->kinematic.velocity.y *= 0.8;
|
|
if (!on_ground){
|
|
player->state = JUMPING;
|
|
}
|
|
else{
|
|
player->state = RUNNING;
|
|
dash_count = 1;
|
|
jumps = 1;printf("\n");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// This accounts for player leaving the ground without jumping (dashing or falling)
|
|
// Must be done before updating on_ground status
|
|
// Possible change: store the previous on_ground state
|
|
if (on_ground == true && !place_meeting(&player->kinematic, (Vector2){0,1}) && player->state != JUMP_SQUAT){
|
|
jumps = 0;
|
|
if (player->state != DASHING)
|
|
player->state = FALLING;
|
|
else
|
|
player->state = JUMPING;
|
|
}
|
|
|
|
on_ground = place_meeting(&player->kinematic, (Vector2){0,1});
|
|
|
|
// This check is to add gravity, but maybe could be combined with above
|
|
if (!on_ground){
|
|
accel.y = GRAV;
|
|
if(player->state != DASHING && player->kinematic.velocity.y > 0)
|
|
player->state = FALLING;
|
|
}
|
|
|
|
// Air friction is less than ground friction
|
|
if (on_ground == true)
|
|
accel.x -= player->kinematic.velocity.x * 8.0;
|
|
else
|
|
accel.x -= player->kinematic.velocity.x * 4.0;
|
|
|
|
// Handle wall jumping
|
|
// TODO: define the wall jump values
|
|
if (IsKeyPressed(JUMP)){
|
|
if (on_ground == false){
|
|
bool left_check = place_meeting(&player->kinematic, (Vector2){-8,0});
|
|
bool right_check = place_meeting(&player->kinematic, (Vector2){8,0});
|
|
if (left_check || right_check){
|
|
player->kinematic.velocity.y = -350;
|
|
player->kinematic.velocity.x = left_check? 400 : -400;
|
|
player->state = JUMPING;
|
|
afterimage_fcounter = 5;
|
|
afterimage_c.r = 128;
|
|
afterimage_c.g = 128;
|
|
afterimage_c.b = 128;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle dashing
|
|
if (IsKeyPressed(DASH) && dash_count > 0){
|
|
// Determine the direction of dashing
|
|
dash_vec.x = 0;
|
|
if (IsKeyDown(RIGHT))
|
|
++dash_vec.x;
|
|
if (IsKeyDown(LEFT))
|
|
--dash_vec.x;
|
|
|
|
dash_vec.y = 0;
|
|
if (IsKeyDown(DOWN))
|
|
++dash_vec.y;
|
|
if (IsKeyDown(UP))
|
|
--dash_vec.y;
|
|
|
|
// Default a direction
|
|
if (dash_vec.x == 0 && dash_vec.y == 0){
|
|
dash_vec.x = sign(player->kinematic.velocity.x);
|
|
if (place_meeting(&player->kinematic, (Vector2){dash_vec.x,0}))
|
|
dash_vec.y = -1;
|
|
}
|
|
|
|
// Apply the scalar value, normalised to the unit direction
|
|
dash_vec = Vector2Scale(Vector2Normalize(dash_vec), DASH_SPD);
|
|
--dash_count;
|
|
frame_counter=0;
|
|
afterimage_fcounter=0;
|
|
afterimage_c.r = 0;
|
|
afterimage_c.g = 0;
|
|
afterimage_c.b = 255;
|
|
player->state = DASHING;
|
|
}
|
|
|
|
// All velocity modification must be done before calling the move function
|
|
move(&player->kinematic, accel);
|
|
player->kinematic.x_shear = -player->kinematic.velocity.x / 600;
|
|
// Handle jumping
|
|
if (IsKeyPressed(JUMP) && jumps > 0){
|
|
player->state = JUMP_SQUAT;
|
|
short_hop = false;
|
|
--jumps;
|
|
}
|
|
|
|
// Deform player based on falling speed
|
|
player->kinematic.set_dim_reduction[1] = 0;
|
|
player->kinematic.set_dim_reduction[3] = 0;
|
|
if (on_ground == false){
|
|
if (player->kinematic.velocity.y < 0)
|
|
player->kinematic.set_dim_reduction[3] = - fmax(player->kinematic.velocity.y / 40, -32) ;
|
|
if (player->kinematic.velocity.y > 0)
|
|
player->kinematic.set_dim_reduction[1] = fmin(player->kinematic.velocity.y / 40, 32);
|
|
|
|
set_squish_target_offset(player->image, 1, player->kinematic.velocity.y / 30);
|
|
set_squish_target_offset(player->image, 3, -player->kinematic.velocity.y / 30);
|
|
}
|
|
|
|
//Generate afterimages
|
|
if (afterimage_fcounter < afterimage_frames){
|
|
if(afterimage_fcounter%2 == 0)
|
|
create_afterimage(player, afterimage_c);
|
|
++afterimage_fcounter;
|
|
}
|
|
|
|
// Set the hitbox reductions
|
|
adjust_hitbox(&player->kinematic);
|
|
|
|
if (dash_count == 0)
|
|
player->image->color.b = 255;
|
|
else
|
|
player->image->color.b = 0;
|
|
}
|
|
|
|
void create_afterimage(struct player_obj *player, Color color){
|
|
struct afterImage *img = malloc(sizeof(struct afterImage));
|
|
if (img){
|
|
for (int i=0;i<=BEZIER_POINTS;++i){
|
|
img->top_vertices[i] = player->image->top_vertices[i];
|
|
img->bottom_vertices[i] = player->image->bottom_vertices[i];
|
|
img->left_vertices[i] = player->image->left_vertices[i];
|
|
img->right_vertices[i] = player->image->right_vertices[i];
|
|
}
|
|
img->pos = (Vector2){player->image->center.x, player->image->center.y};
|
|
img->velocity = (Vector2){player->kinematic.velocity.x, player->kinematic.velocity.y};
|
|
img->opacity = 1.0;
|
|
img->color = color;
|
|
img->prev = NULL;
|
|
if (player->after_img_head == NULL){
|
|
img->next = NULL;
|
|
player->after_img_head = img;
|
|
player->after_img_tail = img;
|
|
}else{
|
|
img->next = player->after_img_head;
|
|
player->after_img_head->prev = img;
|
|
player->after_img_head = img;
|
|
}
|
|
}
|
|
}
|
|
|
|
void remove_last_afterimage(struct player_obj *player){
|
|
struct afterImage *last;
|
|
if (player->after_img_tail != NULL){
|
|
if (player->after_img_tail->opacity <= 0){
|
|
last = player->after_img_tail;
|
|
player->after_img_tail = player->after_img_tail->prev;
|
|
last->prev = NULL;
|
|
free(last);
|
|
}
|
|
// This happens if the last tail is the head
|
|
if (player->after_img_tail == NULL){
|
|
player->after_img_head = NULL;
|
|
}else{
|
|
player->after_img_tail->next = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void free_afterimages(struct player_obj *player){
|
|
struct afterImage *current = player->after_img_head;
|
|
struct afterImage *next;
|
|
while(current != NULL){
|
|
next = current->next;
|
|
current->next = NULL;
|
|
current->prev = NULL;
|
|
free(current);
|
|
current = next;
|
|
}
|
|
player->after_img_head = NULL;
|
|
player->after_img_tail = NULL;
|
|
}
|