183 lines
5 KiB
C++
183 lines
5 KiB
C++
#include "player_states.h"
|
|
#include "scene/animation/animation_tree.h"
|
|
#include "scene/main/viewport.h"
|
|
|
|
void PlayerState::_bind_methods() {}
|
|
|
|
void PlayerState::ready() {
|
|
this->anim = cast_to<AnimationTree>(get_node(NodePath("%AnimationTree")));
|
|
this->anim_fsm = this->anim->get("parameters/playback");
|
|
}
|
|
|
|
void PlayerState::_notification(int what) {
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
|
return;
|
|
}
|
|
switch (what) {
|
|
default:
|
|
return;
|
|
case NOTIFICATION_READY:
|
|
ready();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PlayerState::apply_root_motion(double delta) {
|
|
PlayerBody *body{ get_body() };
|
|
Basis basis{ body->get_global_basis() };
|
|
Vector3 root_motion{ this->anim->get_root_motion_position() };
|
|
body->set_velocity((root_motion.x * basis.get_column(0) + root_motion.z * basis.get_column(2)) / delta);
|
|
}
|
|
|
|
void PlayerState::input_trigger_entry_actions(Ref<InputEvent> const &what) {
|
|
if (what->is_action_pressed("light_attack")) {
|
|
switch_state("LightAttack");
|
|
}
|
|
}
|
|
|
|
void PlayerIdleState::_bind_methods() {}
|
|
|
|
void PlayerIdleState::_notification(int what) {
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
|
return;
|
|
}
|
|
switch (what) {
|
|
default:
|
|
return;
|
|
case NOTIFICATION_PROCESS:
|
|
get_body()->set_velocity(get_body()->get_velocity().move_toward(Vector3(), get_process_delta_time() * 100.f));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PlayerIdleState::state_entered() {
|
|
set_process_unhandled_input(true);
|
|
set_process(true);
|
|
if (!get_body()->get_transformed_movement_input().is_zero_approx()) {
|
|
switch_state<PlayerRunState>();
|
|
}
|
|
}
|
|
|
|
void PlayerIdleState::state_exited() {
|
|
set_process_unhandled_input(false);
|
|
set_process(false);
|
|
}
|
|
|
|
void PlayerIdleState::unhandled_input(Ref<InputEvent> const &what) {
|
|
if (what->is_action("move_left") || what->is_action("move_right") || what->is_action("move_forward") || what->is_action("move_backward")) {
|
|
switch_state<PlayerRunState>();
|
|
} else {
|
|
input_trigger_entry_actions(what);
|
|
}
|
|
}
|
|
|
|
void PlayerRunState::_bind_methods() {
|
|
}
|
|
|
|
void PlayerRunState::process(double delta) {
|
|
apply_root_motion(delta);
|
|
if (get_body()->get_transformed_movement_input().is_zero_approx()) {
|
|
switch_state<PlayerIdleState>();
|
|
} else {
|
|
double angle{ get_body()->get_basis().get_column(2).signed_angle_to(get_body()->get_transformed_movement_input(), { 0.f, 1.f, 0.f }) };
|
|
double motion{ angle * delta * this->rotation_speed };
|
|
if (Math::abs(motion) < Math::abs(angle)) {
|
|
get_body()->rotate_y(motion);
|
|
} else {
|
|
get_body()->look_at(get_body()->get_global_position() - get_body()->get_transformed_movement_input());
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlayerRunState::_notification(int what) {
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
|
return;
|
|
}
|
|
switch (what) {
|
|
default:
|
|
return;
|
|
case NOTIFICATION_PROCESS:
|
|
process(get_process_delta_time());
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PlayerRunState::unhandled_input(Ref<InputEvent> const &what) {
|
|
input_trigger_entry_actions(what);
|
|
}
|
|
|
|
void PlayerRunState::state_entered() {
|
|
set_process(true);
|
|
set_process_unhandled_input(true);
|
|
get_anim_fsm()->travel("run");
|
|
}
|
|
|
|
void PlayerRunState::state_exited() {
|
|
set_process(false);
|
|
set_process_unhandled_input(false);
|
|
get_anim_fsm()->travel("idle");
|
|
}
|
|
|
|
void PlayerBasicAttackState::_bind_methods() {
|
|
BIND_PROPERTY(Variant::STRING, animation_name);
|
|
BIND_HPROPERTY(Variant::NODE_PATH, next_light_attack_state, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CharacterState");
|
|
BIND_PROPERTY(Variant::FLOAT, trigger_next_margin);
|
|
BIND_PROPERTY(Variant::FLOAT, input_next_margin);
|
|
}
|
|
|
|
void PlayerBasicAttackState::process(double delta) {
|
|
apply_root_motion(delta);
|
|
|
|
if (!this->animation_started) {
|
|
if (get_anim_fsm()->get_current_node() == this->animation_name) {
|
|
this->animation_started = true;
|
|
this->anim_length = get_anim_fsm()->get_current_length();
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
delta_since_start += delta;
|
|
float const trigger_next_progress{ this->anim_length - this->trigger_next_margin };
|
|
if (this->next_queued && this->delta_since_start >= trigger_next_progress && !this->next_light_attack_state.is_empty()) {
|
|
switch_state(this->next_light_attack_state);
|
|
} else if (get_anim_fsm()->get_current_node() != this->animation_name) {
|
|
switch_state<PlayerIdleState>();
|
|
}
|
|
}
|
|
|
|
void PlayerBasicAttackState::_notification(int what) {
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
|
return;
|
|
}
|
|
switch (what) {
|
|
default:
|
|
return;
|
|
case NOTIFICATION_PROCESS:
|
|
process(get_process_delta_time());
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PlayerBasicAttackState::unhandled_input(Ref<InputEvent> const &what) {
|
|
float const input_next_progress{ get_anim_fsm()->get_current_length() - this->input_next_margin };
|
|
if (this->delta_since_start > input_next_progress && what->is_action_pressed("light_attack")) {
|
|
this->next_queued = true;
|
|
get_viewport()->set_input_as_handled();
|
|
}
|
|
}
|
|
|
|
void PlayerBasicAttackState::state_entered() {
|
|
set_process(true);
|
|
set_process_unhandled_input(true);
|
|
|
|
this->next_queued = false;
|
|
this->animation_started = false;
|
|
this->delta_since_start = 0.0;
|
|
|
|
get_anim_fsm()->start(this->animation_name);
|
|
}
|
|
|
|
void PlayerBasicAttackState::state_exited() {
|
|
set_process(false);
|
|
set_process_unhandled_input(false);
|
|
}
|