A 3d fps game made in OpenGL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
Symmetry/src/game/player.c

375 lines
14 KiB

#include "player.h"
#include "scene.h"
#include "input.h"
#include "../common/utils.h"
#include "transform.h"
#include "camera.h"
#include "bounding_volumes.h"
#include "../common/hashmap.h"
#include "../common/log.h"
#include "entity.h"
#include "../system/config_vars.h"
#include "../system/platform.h"
#include "game.h"
#include "debug_vars.h"
#include "geometry.h"
#include "im_render.h"
#include "event.h"
#include "sound_source.h"
#include "entity.h"
#include <float.h>
#include <string.h>
static void player_on_mousebutton_released(const struct Event* event);
static void player_on_input_map_released(const struct Event* event);
void player_init(struct Player* player, struct Scene* scene)
{
struct Game_State* game_state = game_state_get();
entity_init(player, "Player", &scene->root_entity);
player->base.flags |= EF_ACTIVE;
player->base.id = 1;
player->base.type = ET_PLAYER;
player->base.bounding_box.min = (vec3){ -1.5f, -1.5f, -1.0f };
player->base.bounding_box.max = (vec3){ 1.5f, 1.5f, 1.0f };
struct Hashmap* config = game_state->cvars;
player->move_speed = hashmap_float_get(config, "player_move_speed");
player->move_speed_multiplier = hashmap_float_get(config, "player_move_speed_multiplier");
player->turn_speed = hashmap_float_get(config, "player_turn_speed");
player->jump_speed = hashmap_float_get(config, "player_jump_speed");
player->gravity = hashmap_float_get(config, "player_gravity");
player->min_downward_distance = hashmap_float_get(config, "player_min_downward_distance");
player->min_forward_distance = hashmap_float_get(config, "player_min_forward_distance");
player->grounded = true;
player->can_jump = true;
player->health = 100;
player->key_mask = 0;
player->mesh = scene_static_mesh_create(scene, "Player_Mesh", player, "sphere.symbres", MAT_BLINN);
struct Camera* player_camera = &scene->cameras[CAM_GAME];
entity_rename(player_camera, "Player_Camera");
player_camera->base.flags |= EF_ACTIVE;
player_camera->clear_color.x = 0.6f;
player_camera->clear_color.y = 0.6f;
player_camera->clear_color.z = 0.9f;
player_camera->clear_color.w = 1.f;
player->camera = player_camera;
struct Sound_Source* weapon_sound = scene_sound_source_create(scene, "Player_Weapon_Sound_Source", player, "sounds/bullet_1.wav", ST_WAV, false, false);
if(weapon_sound)
player->weapon_sound = weapon_sound;
else
log_error("player:init", "Could not add weapon entity to player");
struct Sound_Source* footstep_sound = scene_sound_source_create(scene, "Player_Footstep_Sound_Source", player, "sounds/player_walk.wav", ST_WAV, false, false);
if(footstep_sound)
player->footstep_sound = footstep_sound;
else
log_error("player:init", "Could not add footstep entity to player");
struct Sound_Source* grunt_sound = scene_sound_source_create(scene, "Player_Grunt_Sound_Source", player, "sounds/player_jump_grunt.wav", ST_WAV, false, false);
if(grunt_sound)
player->grunt_sound = grunt_sound;
else
log_error("player:init", "Could not add grunt entity to player");
// Mark player camera and mesh as transient for now. We don't need to save them to file since we recreate them here anyway
player->camera->base.flags |= EF_TRANSIENT;
player->mesh->base.flags |= EF_TRANSIENT;
player->weapon_sound->base.flags |= EF_TRANSIENT;
player->footstep_sound->base.flags |= EF_TRANSIENT;
player->grunt_sound->base.flags |= EF_TRANSIENT;
transform_parent_set(player_camera, player, true);
vec3 cam_translation = {0.f, 1.5f, 0.f};
transform_translate(player_camera, &cam_translation, TS_LOCAL);
sound_listener_set(game_state->sound, player_camera);
sound_listener_update(game_state->sound);
event_manager_subscribe(game_state->event_manager, EVT_MOUSEBUTTON_RELEASED, &player_on_mousebutton_released);
event_manager_subscribe(game_state->event_manager, EVT_INPUT_MAP_RELEASED, &player_on_input_map_released);
}
void player_destroy(struct Player* player)
{
event_manager_unsubscribe(game_state_get()->event_manager, EVT_MOUSEBUTTON_RELEASED, &player_on_mousebutton_released);
event_manager_unsubscribe(game_state_get()->event_manager, EVT_INPUT_MAP_RELEASED, &player_on_input_map_released);
entity_reset(player, player->base.id);
scene_entity_base_remove(game_state_get()->scene, &player->base);
player->base.flags = EF_NONE;
}
void player_update_physics(struct Player* player, struct Scene* scene, float fixed_dt)
{
struct Sound* sound = game_state_get()->sound;
/* Look around */
float total_pitch = quat_get_pitch(&player->camera->base.transform.rotation);
float pitch = 0.f;
float yaw = 0.f;
float max_pitch = 80.f;
vec3 rot_axis_pitch = { 1, 0, 0 };
vec3 rot_axis_yaw = { 0, 1, 0 };
if(input_map_state_get("Turn_Up", KS_PRESSED)) pitch += player->turn_speed;
if(input_map_state_get("Turn_Down", KS_PRESSED)) pitch -= player->turn_speed;
if(input_map_state_get("Turn_Right", KS_PRESSED)) yaw += player->turn_speed;
if(input_map_state_get("Turn_Left", KS_PRESSED)) yaw -= player->turn_speed;
int cursor_yaw, cursor_pitch;
input_mouse_delta_get(&cursor_yaw, &cursor_pitch);
if(input_mouse_mode_get() != MM_RELATIVE)
{
input_mouse_mode_set(MM_RELATIVE);
cursor_yaw = cursor_pitch = 0;
}
pitch = -cursor_pitch * player->turn_speed * fixed_dt;
yaw = cursor_yaw * player->turn_speed * fixed_dt;
total_pitch += pitch;
if(total_pitch >= max_pitch)
{
total_pitch = max_pitch;
pitch = 0.f;
}
else if(total_pitch <= -max_pitch)
{
total_pitch = -max_pitch;
pitch = 0.f;
}
if(yaw != 0.f)
transform_rotate(player, &rot_axis_yaw, -yaw, TS_WORLD);
if(pitch != 0.f)
transform_rotate(player->camera, &rot_axis_pitch, pitch, TS_LOCAL);
/* Movement */
float move_speed = player->move_speed;
vec3 move_direction = { 0.f };
static bool jumping = false;
static bool landed = false;
static float move_speed_vertical = 0.f;
// If we started jumping last frame, set jumpig to false
if(jumping) jumping = false;
if(landed) landed = false;
if(input_map_state_get("Sprint", KS_PRESSED)) move_speed *= player->move_speed_multiplier;
if(input_map_state_get("Move_Forward", KS_PRESSED)) move_direction.z -= 1.f;
if(input_map_state_get("Move_Backward", KS_PRESSED)) move_direction.z += 1.f;
if(input_map_state_get("Move_Left", KS_PRESSED)) move_direction.x -= 1.f;
if(input_map_state_get("Move_Right", KS_PRESSED)) move_direction.x += 1.f;
if(input_map_state_get("Jump", KS_PRESSED))
{
if(player->grounded && player->can_jump)
{
move_speed_vertical += player->jump_speed;
jumping = true;
player->grounded = false;
player->can_jump = false;
}
else
{
player->can_jump = false;
}
}
vec3_norm(&move_direction, &move_direction);
if(move_direction.x != 0 || move_direction.z != 0)
quat_mul_vec3(&move_direction, &player->base.transform.rotation, &move_direction);
// Check for collisions in the directions we want to move
int mouse_x = 0, mouse_y = 0;
platform_mouse_position_get(&mouse_x, &mouse_y);
struct Ray forward_ray = { 0 };
transform_get_absolute_position(player, &forward_ray.origin);
vec3_assign(&forward_ray.direction, &move_direction);
// Get all the entities that intersect then check the distance if it is less than
// or equal to min_collision_distance then we are colliding
struct Raycast_Result ray_result;
scene_ray_intersect(scene, &forward_ray, &ray_result, ERM_STATIC_MESH);
debug_vars_show_int("Colliding Entities", ray_result.num_entities_intersected);
if(ray_result.num_entities_intersected > 0)
{
for(int i = 0; i < ray_result.num_entities_intersected; i++)
{
struct Entity* colliding_entity = ray_result.entities_intersected[i];
float distance = bv_distance_ray_bounding_box(&forward_ray, &colliding_entity->derived_bounding_box);
if(distance > 0.f && distance <= player->min_forward_distance && colliding_entity != player->mesh)
{
vec3 intersection_point = forward_ray.direction;
vec3_scale(&intersection_point, &intersection_point, distance);
vec3_add(&intersection_point, &intersection_point, &forward_ray.origin);
struct Bounding_Box* box = &colliding_entity->derived_bounding_box;
vec3 normal = bv_bounding_box_normal_from_intersection_point(box, intersection_point);
im_ray_origin_dir(intersection_point, normal, 5.f, (vec4) { 1.f, 0.f, 0.f, 1.f }, 3);
im_ray(&forward_ray, player->min_forward_distance, (vec4) { 0.f, 1.f, 0.f, 1.f }, 3);
float dot = (vec3_dot(&move_direction, &normal));
vec3 norm_scaled = { 0.f };
vec3_scale(&normal, &normal, dot);
vec3_sub(&move_direction, &move_direction, &normal);
debug_vars_show_vec3("Normal", &normal);
debug_vars_show_vec3("Dir", &move_direction);
debug_vars_show_float("Dot", dot);
}
}
}
// Check for collisions below
move_speed_vertical += player->gravity;
struct Raycast_Result down_ray_result;
struct Ray downward_ray;
transform_get_absolute_position(player->mesh, &downward_ray.origin);
vec3_fill(&downward_ray.direction, 0.f, -1.f, 0.f);
scene_ray_intersect(scene, &downward_ray, &down_ray_result, ERM_STATIC_MESH);
if(down_ray_result.num_entities_intersected > 0)
{
for(int i = 0; i < down_ray_result.num_entities_intersected; i++)
{
struct Entity* colliding_entity = down_ray_result.entities_intersected[i];
if(colliding_entity == player->mesh)
continue;
float distance = bv_distance_ray_bounding_box(&downward_ray, &colliding_entity->derived_bounding_box);
if(distance > 0.f && distance <= player->min_downward_distance && !jumping)
{
move_speed_vertical = 0.f;
if(!player->grounded)
{
player->grounded = true;
landed = true;
}
}
}
}
// Apply speed to direction then translate
vec3 translation = {0.f, 0.f, 0.f};
vec3_assign(&translation, &move_direction);
translation.x *= move_speed * fixed_dt;
translation.z *= move_speed * fixed_dt;
translation.y = move_speed_vertical * fixed_dt;
transform_translate(player, &translation, TS_WORLD);
// Sounds
if(translation.x != 0.f || translation.z != 0.f || landed)
{
if(player->grounded)
{
if(sound_source_is_paused(sound, player->footstep_sound)) // if a sound is already playing, let it finish first then switch to avoid abrupt transition
{
// Sprinting/Walking/Jump Landing
if(landed)
{
if(strncmp(player->footstep_sound->source_buffer->filename, "sounds/player_jump_land.wav", MAX_FILENAME_LEN) != 0)
sound_source_buffer_set(sound, player->footstep_sound, "sounds/player_jump_land.wav", ST_WAV);
}
else if(input_map_state_get("Sprint", KS_PRESSED))
{
if(strncmp(player->footstep_sound->source_buffer->filename, "sounds/player_sprint.wav", MAX_FILENAME_LEN) != 0)
sound_source_buffer_set(sound, player->footstep_sound, "sounds/player_sprint.wav", ST_WAV);
}
else if(strncmp(player->footstep_sound->source_buffer->filename, "sounds/player_walk.wav", MAX_FILENAME_LEN) != 0)
{
sound_source_buffer_set(sound, player->footstep_sound, "sounds/player_walk.wav", ST_WAV);
}
sound_source_play(sound, player->footstep_sound);
}
}
}
if(jumping)
{
if(sound_source_is_paused(sound, player->grunt_sound))
{
if(strncmp(player->grunt_sound->source_buffer->filename, "sounds/player_jump_grunt.wav", MAX_FILENAME_LEN) != 0)
sound_source_buffer_set(sound, player->grunt_sound, "sounds/player_jump_grunt.wav", ST_WAV);
sound_source_play(sound, player->grunt_sound);
}
}
debug_vars_show_bool("Grounded", player->grounded);
}
void player_on_input_map_released(const struct Event* event)
{
struct Game_State* game_state = game_state_get();
if(strncmp("Jump", event->input_map.name, HASH_MAX_KEY_LEN) == 0)
{
struct Player* player = &game_state->scene->player;
player->can_jump = true;
}
}
void player_on_mousebutton_released(const struct Event* event)
{
int button = event->mousebutton.button;
int state = event->mousebutton.state;
struct Game_State* game_state = game_state_get();
struct Scene* scene = game_state->scene;
struct Player* player = &scene->player;
if(game_state->game_mode != GAME_MODE_GAME)
return;
/* Aiming and Projectiles*/
if(button == MSB_LEFT)
{
log_message("Right Click");
int half_width = 0, half_height = 0;
window_get_drawable_size(game_state->window, &half_width, &half_height);
half_width /= 2;
half_height /= 2;
struct Ray bullet_ray = camera_screen_coord_to_ray(player->camera, half_width, half_height);
struct Entity* colliding_entity = scene_ray_intersect_closest(scene, &bullet_ray, ERM_STATIC_MESH);
if(!colliding_entity || colliding_entity == player->mesh)
return;
float distance = bv_distance_ray_bounding_box(&bullet_ray, &colliding_entity->derived_bounding_box);
if(distance > 0.f)
{
vec3 collision_point = bullet_ray.direction;
vec3_scale(&collision_point, &collision_point, distance);
vec3_add(&collision_point, &collision_point, &bullet_ray.origin);
//struct Static_Mesh* bullet = scene_static_mesh_create(game_state_get()->scene, "bullet", NULL, "cube.symbres", MAT_UNSHADED);
struct Light* bullet = entity_load("Spot", DIRT_INSTALL, true);
if(bullet) transform_set_position(bullet, &collision_point);
sound_source_play(game_state->sound, player->weapon_sound);
}
}
}
void player_apply_damage(struct Player* player, struct Enemy* enemy)
{
log_message("Player hit!");
player->health -= enemy->damage;
if(player->health <= 0)
{
log_message("Player Ded!");
struct Event_Manager* event_manager = game_state_get()->event_manager;
struct Event* player_death_event = event_manager_create_new_event(event_manager);
player_death_event->type = EVT_PLAYER_DIED;
player_death_event->player_death.player = player;
player_death_event->player_death.enemy = enemy;
event_manager_send_event(event_manager, player_death_event);
}
}