202 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "player.h"
 | |
| #include "animation_player.h"
 | |
| #include "state_machine.h"
 | |
| #include <SDL2/SDL_events.h>
 | |
| #include <SDL2/SDL_keyboard.h>
 | |
| #include <SDL2/SDL_scancode.h>
 | |
| #include <SDL2/SDL_render.h>
 | |
| #include <stdbool.h>
 | |
| #include <stdint.h>
 | |
| 
 | |
| #define SPRITE_WIDTH 100
 | |
| #define SPRITE_HEIGHT 64
 | |
| 
 | |
| void  get_player_controls(Player *player);
 | |
| State *update_state      (Player *player, uint32_t state);
 | |
| State *idle_state        (StateMachine *sm, Player *player);
 | |
| State *walk_state        (StateMachine *sm, Player *player);
 | |
| State *dash_state        (StateMachine *sm, Player *player);
 | |
| State *jump_state        (StateMachine *sm, Player *player);
 | |
| State *fall_state        (StateMachine *sm, Player *player);
 | |
| 
 | |
| void player_init(Player *player, SDL_Renderer *renderer, SDL_Rect position) {
 | |
| 	player->base_y      = position.y;
 | |
| 	player->position    = position;
 | |
| 	player->movement    = (Movement){0};
 | |
| 	player->x_direction = PLAYER_DIRECTION_RIGHT;
 | |
| 	player->velocity    = 4;
 | |
| 
 | |
| 	player->animations[PLAYER_STATE_IDLE] = 
 | |
| 			ap_init(renderer, "knight_player/Idle_KG_2.png",    125, SPRITE_WIDTH, SPRITE_HEIGHT, true);
 | |
| 	player->animations[PLAYER_STATE_WALK] = 
 | |
| 			ap_init(renderer, "knight_player/Walking_KG_2.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, true);
 | |
| 	player->animations[PLAYER_STATE_DASH] = 
 | |
| 			ap_init(renderer, "knight_player/Dashing_KG_1.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, false);
 | |
| 	player->animations[PLAYER_STATE_JUMP] = 
 | |
| 			ap_init(renderer, "knight_player/Jump_KG_2.png",    100, SPRITE_WIDTH, SPRITE_HEIGHT, false);
 | |
| 	player->animations[PLAYER_STATE_FALL] = 
 | |
| 			ap_init(renderer, "knight_player/Fall_KG_2.png",     80, SPRITE_WIDTH, SPRITE_HEIGHT, false);
 | |
| 
 | |
| 	player->states[PLAYER_STATE_IDLE] = (State){ .state_func = (StateFunc *)idle_state };
 | |
| 	player->states[PLAYER_STATE_WALK] = (State){ .state_func = (StateFunc *)walk_state };
 | |
| 	player->states[PLAYER_STATE_DASH] = (State){ .state_func = (StateFunc *)dash_state };
 | |
| 	player->states[PLAYER_STATE_JUMP] = (State){ .state_func = (StateFunc *)jump_state };
 | |
| 	player->states[PLAYER_STATE_FALL] = (State){ .state_func = (StateFunc *)fall_state };
 | |
| 
 | |
| 	player->state_machine = (StateMachine){ .current_state = &(player->states[PLAYER_STATE_IDLE]) };
 | |
| 	player->current_state = PLAYER_STATE_IDLE;
 | |
| 	player->controls      = (PlayerControl){0};
 | |
| }
 | |
| 
 | |
| void player_update(Player *player, uint32_t ticks) {
 | |
| 	get_player_controls(player);
 | |
| 
 | |
| 	if (player->controls.walk_left) {
 | |
| 		player->movement.x  = PLAYER_DIRECTION_LEFT * player->velocity;
 | |
| 		player->x_direction = PLAYER_DIRECTION_LEFT;
 | |
| 	} else if (player->controls.walk_right) {
 | |
| 		player->movement.x  = PLAYER_DIRECTION_RIGHT * player->velocity;
 | |
| 		player->x_direction = PLAYER_DIRECTION_RIGHT;
 | |
| 	} else if (!(player->controls.walk_left) && !(player->controls.walk_right)) {
 | |
| 		player->movement.x = 0;
 | |
| 	}
 | |
| 
 | |
| 	sm_run(&(player->state_machine), (void *)player);
 | |
| 
 | |
| 	player->position.x += player->movement.x;
 | |
| 	player->position.y += player->movement.y;
 | |
| 	if (player->position.y > player->base_y) {
 | |
| 		player->position.y = player->base_y;
 | |
| 	}
 | |
| 	ap_update(&(player->animations[player->current_state]), ticks);
 | |
| }
 | |
| 
 | |
| void player_draw(const Player *player, SDL_Renderer *renderer) {
 | |
| 	ap_draw(renderer, &(player->animations[player->current_state]), &(player->position), player->x_direction == PLAYER_DIRECTION_LEFT);
 | |
| }
 | |
| 
 | |
| void get_player_controls(Player *player) {
 | |
| 	const uint8_t *keys = SDL_GetKeyboardState(NULL);
 | |
| 
 | |
| 	player->controls.walk_left  = keys[SDL_SCANCODE_LEFT]  == SDL_PRESSED;
 | |
| 	player->controls.walk_right = keys[SDL_SCANCODE_RIGHT] == SDL_PRESSED;
 | |
| 
 | |
| 	player->controls.space_last    = player->controls.space_pressed;
 | |
| 	player->controls.space_pressed = keys[SDL_SCANCODE_SPACE] == SDL_PRESSED;
 | |
| 	player->controls.jump          = player->controls.space_last && !(player->controls.space_pressed);
 | |
| 
 | |
| 	player->controls.lctrl_last    = player->controls.lctrl_pressed;
 | |
| 	player->controls.lctrl_pressed = keys[SDL_SCANCODE_LCTRL] == SDL_PRESSED;
 | |
| 	player->controls.dash          = player->controls.lctrl_last && !(player->controls.lctrl_pressed);
 | |
| }
 | |
| 
 | |
| State *update_state(Player *player, uint32_t state) {
 | |
| 	if (state >= COUNT_PLAYER_STATES) {
 | |
| 		return &(player->states[player->current_state]);
 | |
| 	}
 | |
| 
 | |
| 	player->current_state = state;
 | |
| 	return &(player->states[state]);
 | |
| }
 | |
| 
 | |
| State *idle_state(StateMachine *sm, Player *player) {
 | |
| 	uint32_t state = PLAYER_STATE_IDLE;
 | |
| 
 | |
| 	if (player->controls.jump) {
 | |
| 		player->movement.y = player->velocity * 6 * -1;
 | |
| 		state              = PLAYER_STATE_JUMP;
 | |
| 	} else if (player->controls.walk_left) {
 | |
| 		state = PLAYER_STATE_WALK;
 | |
| 	} else if (player->controls.walk_right) {
 | |
| 		state = PLAYER_STATE_WALK;
 | |
| 	}
 | |
| 
 | |
| 	return update_state(player, state);
 | |
| }
 | |
| 
 | |
| State *walk_state(StateMachine *sm, Player *player) {
 | |
| 	uint32_t state         = PLAYER_STATE_WALK;
 | |
| 
 | |
| 	if (player->controls.jump) {
 | |
| 		player->movement.y = player->velocity * 6 * -1;
 | |
| 		state              = PLAYER_STATE_JUMP;
 | |
| 	} else if (player->controls.dash) {
 | |
| 		player->controls.dash_in_air = false;
 | |
| 		player->movement.x           = player->x_direction * player->velocity * 2;
 | |
| 		state                        = PLAYER_STATE_DASH;
 | |
| 	}
 | |
| 
 | |
| 	if (!(player->controls.walk_left) && !(player->controls.walk_right)) {
 | |
| 		state = PLAYER_STATE_IDLE;
 | |
| 	}
 | |
| 
 | |
| 	return update_state(player, state);
 | |
| }
 | |
| 
 | |
| State *dash_state(StateMachine *sm, Player *player) {
 | |
| 	bool reset     = false;
 | |
| 	uint32_t state = PLAYER_STATE_DASH;
 | |
| 
 | |
| 	player->movement.x += player->x_direction * 20;
 | |
| 
 | |
| 	if (player->animations[PLAYER_STATE_DASH].finished) {
 | |
| 		if (player->controls.dash_in_air) {
 | |
| 			player->movement.y = player->velocity * 6;
 | |
| 			state              = PLAYER_STATE_FALL;
 | |
| 		} else {
 | |
| 			player->movement.x = 0;
 | |
| 			state              = PLAYER_STATE_IDLE;
 | |
| 		}
 | |
| 
 | |
| 		reset = true;
 | |
| 	}
 | |
| 
 | |
| 	if (reset) {
 | |
| 		ap_reset(&(player->animations[PLAYER_STATE_DASH]));
 | |
| 	}
 | |
| 
 | |
| 	return update_state(player, state);
 | |
| }
 | |
| 
 | |
| State *jump_state(StateMachine *sm, Player *player) {
 | |
| 	bool reset     = false;
 | |
| 	uint32_t state = PLAYER_STATE_JUMP;
 | |
| 
 | |
| 	player->movement.y += player->velocity * 0.5;
 | |
| 
 | |
| 	if (player->controls.dash) {
 | |
| 		player->controls.dash_in_air = true;
 | |
| 		player->movement.x           = player->x_direction * player->velocity * 2;
 | |
| 		player->movement.y           = player->velocity * 0.5;
 | |
| 		reset                        = true;
 | |
| 		state                        = PLAYER_STATE_DASH;
 | |
| 	} else if (player->animations[PLAYER_STATE_JUMP].finished) {
 | |
| 		player->movement.y = player->velocity * 6;
 | |
| 		reset              = true;
 | |
| 		state              = PLAYER_STATE_FALL;
 | |
| 	}
 | |
| 
 | |
| 	if (reset) {
 | |
| 		ap_reset(&(player->animations[PLAYER_STATE_JUMP]));
 | |
| 	}
 | |
| 
 | |
| 	return update_state(player, state);
 | |
| }
 | |
| 
 | |
| State *fall_state(StateMachine *sm, Player *player) {
 | |
| 	bool reset     = false;
 | |
| 	uint32_t state = PLAYER_STATE_FALL;
 | |
| 
 | |
| 	if (player->animations[PLAYER_STATE_FALL].finished) {
 | |
| 		player->movement.y = 0;
 | |
| 		reset              = true;
 | |
| 		state              = PLAYER_STATE_IDLE;
 | |
| 	}
 | |
| 
 | |
| 	if (reset) {
 | |
| 		ap_reset(&(player->animations[PLAYER_STATE_FALL]));
 | |
| 	}
 | |
| 
 | |
| 	return update_state(player, state);
 | |
| }
 |