trenches/src/player.cpp

102 lines
4.4 KiB
C++

#include "player.hpp"
#include "utils/godot_macros.hpp"
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/classes/input_event_joypad_motion.hpp>
void Player::_bind_methods() {
#define CLASSNAME Player
GDFUNCTION(get_input_directions);
}
void Player::_ready() {
if(gd::Engine::get_singleton()->is_editor_hint())
return;
// setup input callbacks
this->input = this->get_node<utils::PlayerInput>("%PlayerInput");
this->input->listen_to(utils::PlayerInput::Listener("dir_left", "dir_right", callable_mp(this, &Player::_on_dir_horizontal)));
this->input->listen_to(utils::PlayerInput::Listener("dir_backward", "dir_forward", callable_mp(this, &Player::_on_dir_vertical)));
this->input->listen_to(utils::PlayerInput::Listener("fire", callable_mp(this, &Player::_on_fire)));
this->input->listen_to(utils::PlayerInput::Listener("run", callable_mp(this, &Player::_on_run)));
// get components
this->anim_tree = this->get_node<PlayerAnimTree>("CharacterModel/AnimationTree");
this->model_node = this->get_node<gd::Node3D>("%CharacterModel");
// setup camera
this->camera_parent = this->get_node<gd::Node3D>("%CameraParent");
this->camera_parent->set_global_rotation(this->get_global_rotation());
}
void Player::_process(double delta) {
if(gd::Engine::get_singleton()->is_editor_hint())
return;
// process rotations
this->process_rotate(delta); // global character rotation
this->process_transform_camera(delta); // camera input rotation
// set the global motion based on model-space motion vector
gd::Basis const &model_basis{this->model_node->get_global_basis()};
this->anim_tree->set_walk_speed(gd::Math::max(0.f, model_basis.get_column(2).dot(this->camera_parent->get_basis().get_column(2))));
gd::Vector3 const local_motion{this->anim_tree->get_root_motion_position()};
gd::Vector3 const motion {
local_motion.x * model_basis.get_column(0) +
local_motion.y * model_basis.get_column(1) +
local_motion.z * model_basis.get_column(2)
+ (this->is_on_floor() ? gd::Vector3{} : gd::Vector3{0.f, -0.05f, 0.f}) // add some gravity if required
};
this->set_velocity(motion / delta); // velocity has to be in m/s, root motion is framerate-dependent. meters/second=distance/time.
}
void Player::_physics_process(double delta [[maybe_unused]]) {
if(gd::Engine::get_singleton()->is_editor_hint())
return;
this->move_and_slide();
}
void Player::damage() {
this->anim_tree->death_animation();
}
void Player::process_transform_camera(double delta) {
this->camera_parent->set_global_position(this->get_global_position());
float const camera_speed{float(delta) * (this->anim_tree->match_tags(PlayerAnimTree::Aim) ? this->AIMING_CAMERA_ROTATION_SPEED : this->CAMERA_ROTATION_SPEED)};
this->camera_parent->rotate_y(this->input_directions.x * -camera_speed);
}
void Player::process_rotate(double delta) {
if(this->anim_tree->match_tags(PlayerAnimTree::Tags::Turn)) {
//! the signed angle difference between the left axes of the camera parent and Player
float const diff{-this->camera_parent->get_global_basis().get_column(0).signed_angle_to(this->get_global_basis().get_column(0), {0.f, 1.f, 0.f})};
float const dir{gd::Math::sign(diff)};
//! the maximum rotation to allow for this frame
float const speed{float(delta) * this->ROTATION_SPEED};
float const actual_speed{speed < gd::Math::abs(diff) ? dir * speed : diff};
// rotate by max allowed or full difference, whichever has the smaller magnitude
this->rotate_y(actual_speed);
this->anim_tree->set_target_turn_speed(gd::Math::clamp(diff * 2.f, -1.f, 1.f) * M_PI_2f *0.9f);
}
}
void Player::_on_dir_horizontal(gd::Ref<gd::InputEvent>, float value) {
this->input_directions.x = value;
}
void Player::_on_dir_vertical(gd::Ref<gd::InputEvent>, float value) {
this->input_directions.y = value;
this->anim_tree->set_aim_weapon(value <= AIM_INPUT_THRESHOLD);
this->anim_tree->set_is_walking(value > WALK_INPUT_THRESHOLD);
}
void Player::_on_fire(gd::Ref<gd::InputEvent> event, float) {
if(event->is_pressed()) {
this->anim_tree->set_fire_weapon();
this->anim_tree->set_stab();
}
}
void Player::_on_run(gd::Ref<gd::InputEvent> event, float) {
if(event->is_pressed())
this->anim_tree->set_is_running();
}
gd::Vector2 Player::get_input_directions() const {
return this->input_directions;
}