diff --git a/modules/going/checkpoint.cpp b/modules/going/checkpoint.cpp new file mode 100644 index 00000000..2a520f89 --- /dev/null +++ b/modules/going/checkpoint.cpp @@ -0,0 +1,65 @@ +#include "checkpoint.h" +#include "core/config/engine.h" +#include "going/player_body.h" +#include "macros.h" + +void Checkpoint::_bind_methods() { + BIND_PROPERTY(Variant::VECTOR3, location); + BIND_PROPERTY(Variant::BOOL, can_jump); + BIND_PROPERTY(Variant::BOOL, can_bash); +} + +void Checkpoint::set_location(Transform3D location) { + this->location = location; +} + +Transform3D Checkpoint::get_location() const { + return this->location; +} + +void Checkpoint::set_can_jump(bool can_jump) { + this->can_jump = can_jump; +} + +bool Checkpoint::get_can_jump() const { + return this->can_jump; +} + +void Checkpoint::set_can_bash(bool can_bash) { + this->can_bash = can_bash; +} + +bool Checkpoint::get_can_bash() const { + return this->can_bash; +} + +void Checkpoint::load(PlayerBody *body) const { + body->set_global_transform(this->location); + body->set_can_jump(this->can_jump); + // body->set_can_bash(this->can_bash); +} + +void Checkpoint::save(PlayerBody *body) { + this->set_location(body->get_global_transform_interpolated()); + this->set_can_jump(body->get_can_jump()); + // self->set_can_bash(body->get_can_bash()); +} + +Ref Checkpoint::save_new(PlayerBody *body) { + Ref self{memnew(Checkpoint)}; + self->save(body); + return self; +} + +void CheckpointArea::_notification(int what) { + if(!Engine::get_singleton()->is_editor_hint() && what == NOTIFICATION_READY) { + this->connect(this->body_entered, callable_mp(this, &self_type::on_body_entered)); + } +} + +void CheckpointArea::on_body_entered(Node3D *body) { + if(PlayerBody *player{Object::cast_to(body)}) { + player->save_checkpoint(); + } +} + diff --git a/modules/going/checkpoint.h b/modules/going/checkpoint.h new file mode 100644 index 00000000..53afddd9 --- /dev/null +++ b/modules/going/checkpoint.h @@ -0,0 +1,37 @@ +#ifndef CHECKPOINT_H +#define CHECKPOINT_H + +#include "core/io/resource.h" +#include "core/math/transform_3d.h" +#include "scene/3d/node_3d.h" +#include "scene/3d/physics/area_3d.h" +class PlayerBody; + +class Checkpoint : public Resource { + GDCLASS(Checkpoint, Resource); + static void _bind_methods(); +public: + void set_location(Transform3D location); + Transform3D get_location() const; + void set_can_jump(bool can_jump); + bool get_can_jump() const; + void set_can_bash(bool can_bash); + bool get_can_bash() const; + + void load(PlayerBody *body) const; + void save(PlayerBody *body); + static Ref save_new(PlayerBody *body); +private: + Transform3D location{}; + bool can_jump{false}, can_bash{false}; +}; + +class CheckpointArea : public Area3D { + GDCLASS(CheckpointArea, Area3D); + static void _bind_methods() {} + void _notification(int what); + void on_body_entered(Node3D *body); + StringName body_entered{"body_entered"}; +}; + +#endif // !CHECKPOINT_H diff --git a/modules/going/player_body.cpp b/modules/going/player_body.cpp index 5d2e7df4..8dca37ab 100644 --- a/modules/going/player_body.cpp +++ b/modules/going/player_body.cpp @@ -2,10 +2,12 @@ #include "core/config/engine.h" #include "core/input/input.h" #include "core/math/math_funcs.h" +#include "core/object/class_db.h" +#include "macros.h" +#include "player_states.h" #include "scene/3d/camera_3d.h" #include "scene/animation/animation_player.h" #include "scene/main/viewport.h" -#include "macros.h" char *const PlayerBody::split_step_action{const_cast("split_step")}; char *const PlayerBody::move_left_action{const_cast("move_left")}; @@ -16,6 +18,8 @@ char *const PlayerBody::move_back_action{const_cast("move_back")}; void PlayerBody::_bind_methods() { ClassDB::bind_method(D_METHOD("get_desired_velocity"), &self_type::get_desired_velocity); ClassDB::bind_method(D_METHOD("get_desired_direction"), &self_type::get_desired_direction); + ClassDB::bind_method(D_METHOD("save_checkpoint"), &self_type::save_checkpoint); + ClassDB::bind_method(D_METHOD("load_checkpoint"), &self_type::load_checkpoint); BIND_PROPERTY(Variant::FLOAT, stopping_deceleration); BIND_PROPERTY(Variant::FLOAT, start_speed); @@ -58,6 +62,7 @@ void PlayerBody::enter_tree() { this->anim = Object::cast_to(this->get_node(NodePath("character/AnimationPlayer"))); this->camera = Object::cast_to(this->get_node(NodePath("Camera3D"))); this->camera->set_fov(this->min_fov); + this->last_checkpoint = Checkpoint::save_new(this); } void PlayerBody::process(double delta) { @@ -74,6 +79,15 @@ void PlayerBody::physics_process(double delta) { this->move_and_slide(); } +void PlayerBody::save_checkpoint() { + this->last_checkpoint->save(this); +} + +void PlayerBody::load_checkpoint() { + this->last_checkpoint->load(this); + this->state->force_state(); +} + Vector3 PlayerBody::get_desired_direction() const { Basis const global{this->camera->get_global_basis()}; Vector3 forward{global.get_column(2)}; diff --git a/modules/going/player_body.h b/modules/going/player_body.h index 677dda1a..35426ef6 100644 --- a/modules/going/player_body.h +++ b/modules/going/player_body.h @@ -1,9 +1,11 @@ #ifndef PLAYER_BODY_H #define PLAYER_BODY_H +#include "going/checkpoint.h" #include "scene/3d/camera_3d.h" #include "scene/3d/physics/character_body_3d.h" #include "scene/animation/animation_player.h" +class PlayerStateMachine; class PlayerBody : public CharacterBody3D { GDCLASS(PlayerBody, CharacterBody3D); @@ -14,6 +16,8 @@ class PlayerBody : public CharacterBody3D { void physics_process(double delta); public: + void save_checkpoint(); + void load_checkpoint(); Vector3 get_desired_direction() const; Vector3 get_desired_velocity() const; Vector2 get_movement_input() const; @@ -71,6 +75,8 @@ private: float model_lean_speed{0.25f}; double game_over_speed{1.0/4.0}; bool can_jump{false}; + Ref last_checkpoint{nullptr}; + PlayerStateMachine *state{nullptr}; public: static char *const split_step_action; static char *const move_left_action; diff --git a/modules/going/player_states.cpp b/modules/going/player_states.cpp index 90815bb3..5fd8eff4 100644 --- a/modules/going/player_states.cpp +++ b/modules/going/player_states.cpp @@ -30,7 +30,7 @@ void StandingState::process(double delta) { this->game_over_timer += delta * this->get_body()->get_game_over_speed(); if(this->game_over_timer > 1.0) { RenderingServer::get_singleton()->global_shader_parameter_set(this->game_over_param, 0.0); - SceneTree::get_singleton()->reload_current_scene(); + this->get_body()->load_checkpoint(); } else { RenderingServer::get_singleton()->global_shader_parameter_set(this->game_over_param, float(this->game_over_timer)); this->game_over_timer = MIN(this->game_over_timer, 1.f); @@ -78,7 +78,7 @@ void RunningState::process_lean(double delta) { Vector3 const up{Vector3{0.0, 1.0, 0.0} + Vector3{cross.x, 0.f, cross.y} * this->lean_modifier}; Vector3 const forward{current.x, 0.f, current.z}; if(!forward.is_zero_approx()) { - this->get_body()->get_model()->look_at(this->get_body()->get_global_position() - current, up); + this->get_body()->get_model()->look_at(this->get_body()->get_global_position() - forward, up); } } } @@ -100,18 +100,20 @@ void RunningState::physics_process(double delta) { void RunningState::state_exited() { Vector3 const velocity{this->get_body()->get_velocity()}; - Vector3 const velocity_flat{velocity.x, 0.f, velocity.z}; - if(!velocity_flat.is_zero_approx()) { - this->get_body()->get_model()->look_at(this->get_body()->get_global_position() - velocity_flat); + Vector3 velocity_flat{velocity.x, 0.f, velocity.z}; + if(velocity_flat.is_zero_approx()) { + velocity_flat = this->get_body()->get_model()->get_global_basis().get_column(2); } + this->get_body()->get_model()->look_at(this->get_body()->get_global_position() - velocity_flat); this->lean_modifier = 0.f; } PlayerState::StateID SplitStepState::get_next_state() const { - if(!this->get_body()->is_on_floor()) { - return FallingState::get_class_static(); - } else if(this->get_body()->get_can_jump() && this->jump && this->timer <= 0.0) { + bool const jump_input{this->get_body()->get_can_jump() && this->jump}; + if(jump_input && (this->timer <= 0.0 || !this->get_body()->is_on_floor())) { return JumpingState::get_class_static(); + } else if(!this->get_body()->is_on_floor()) { + return FallingState::get_class_static(); } else if(this->timer <= 0.0) { return RunningState::get_class_static(); } else { @@ -169,7 +171,7 @@ void FallingState::process(double delta) { this->game_over_timer += delta * this->get_body()->get_game_over_speed(); if(this->game_over_timer > 1.0) { RenderingServer::get_singleton()->global_shader_parameter_set(this->game_over_param, 0.0); - SceneTree::get_singleton()->reload_current_scene(); + this->get_body()->load_checkpoint(); } else { RenderingServer::get_singleton()->global_shader_parameter_set(this->game_over_param, float(this->game_over_timer)); this->game_over_timer = MIN(this->game_over_timer, 1.f); diff --git a/modules/going/player_states.h b/modules/going/player_states.h index ce265a1c..f987bb6c 100644 --- a/modules/going/player_states.h +++ b/modules/going/player_states.h @@ -92,7 +92,10 @@ class PlayerStateMachine : public Node { void try_transition(); template void add_state(); - +public: + template + void force_state(); +private: PlayerBody *body{nullptr}; PlayerState *current_state{nullptr}; HashMap states; @@ -100,7 +103,7 @@ class PlayerStateMachine : public Node { template void PlayerStateMachine::add_state() { - PlayerState *state{new TState()}; + PlayerState *state{memnew(TState)}; state->body = this->body; this->states.insert(TState::get_class_static(), state); if(this->current_state == nullptr) { @@ -109,4 +112,9 @@ void PlayerStateMachine::add_state() { } } +template +void PlayerStateMachine::force_state() { + this->states[TState::get_class_static()]; +} + #endif // !PLAYER_STATES_H diff --git a/modules/going/register_types.cpp b/modules/going/register_types.cpp index fe44b840..f40091dc 100644 --- a/modules/going/register_types.cpp +++ b/modules/going/register_types.cpp @@ -1,6 +1,7 @@ #include "register_types.h" #include "core/object/class_db.h" +#include "going/checkpoint.h" #include "going/game_ui.h" #include "going/player_body.h" #include "going/player_states.h" @@ -18,6 +19,8 @@ void initialize_going_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); } void uninitialize_going_module(ModuleInitializationLevel p_level) {