feat: first basic enemy patrolling behaviour
This commit is contained in:
		
							parent
							
								
									7c4c75d193
								
							
						
					
					
						commit
						526f756736
					
				| 
						 | 
					@ -2,3 +2,4 @@ Import('env')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
env.add_source_files(env.modules_sources, "*.cpp")
 | 
					env.add_source_files(env.modules_sources, "*.cpp")
 | 
				
			||||||
env.add_source_files(env.modules_sources, "weapons/*.cpp")
 | 
					env.add_source_files(env.modules_sources, "weapons/*.cpp")
 | 
				
			||||||
 | 
					env.add_source_files(env.modules_sources, "enemies/*.cpp")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										55
									
								
								modules/wave_survival/enemies/enemy_wretched.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								modules/wave_survival/enemies/enemy_wretched.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					#include "enemy_wretched.h"
 | 
				
			||||||
 | 
					#include "wave_survival/npc_unit.h"
 | 
				
			||||||
 | 
					#include "wave_survival/patrol_path.h"
 | 
				
			||||||
 | 
					#include "wave_survival/player_body.h"
 | 
				
			||||||
 | 
					#include "wave_survival/state_machine.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyWretched::_bind_methods() {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyWretched::ready() {
 | 
				
			||||||
 | 
						if (StateMachine * fsm{ cast_to<StateMachine>(get_node(NodePath("%StateMachine"))) }) {
 | 
				
			||||||
 | 
							fsm->add_state(memnew(WretchedPatrolState));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyWretched::_notification(int what) {
 | 
				
			||||||
 | 
						if (Engine::get_singleton()->is_editor_hint()) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch (what) {
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							case NOTIFICATION_READY:
 | 
				
			||||||
 | 
								ready();
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void WretchedState::set_target(Node *node) {
 | 
				
			||||||
 | 
						this->target = cast_to<EnemyWretched>(node);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EnemyWretched *WretchedState::get_target() const {
 | 
				
			||||||
 | 
						return this->target;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void WretchedPatrolState::enter_state() {
 | 
				
			||||||
 | 
						this->nav = get_target()->get_nav();
 | 
				
			||||||
 | 
						this->path = get_target()->get_unit()->get_patrol_path();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float const max_speed{ get_target()->get_unit()->get_patrol_speed() };
 | 
				
			||||||
 | 
						get_target()->set_movement_speed(max_speed);
 | 
				
			||||||
 | 
						this->nav->set_max_speed(max_speed);
 | 
				
			||||||
 | 
						this->path_point = this->path->get_closest_point(get_target()->get_global_position());
 | 
				
			||||||
 | 
						this->nav->set_target_position(this->path->point_at(this->path_point));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void WretchedPatrolState::process(double delta) {
 | 
				
			||||||
 | 
						if (this->nav->is_navigation_finished()) {
 | 
				
			||||||
 | 
							this->path_point += 1;
 | 
				
			||||||
 | 
							this->nav->set_target_position(this->path->point_at(this->path_point));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Vector3 const direction{ get_target()->get_global_position().direction_to(this->nav->get_next_path_position()) };
 | 
				
			||||||
 | 
						get_target()->set_movement_direction(Vector2{ direction.x, direction.z }.normalized());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										45
									
								
								modules/wave_survival/enemies/enemy_wretched.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								modules/wave_survival/enemies/enemy_wretched.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,45 @@
 | 
				
			||||||
 | 
					#ifndef ENEMY_WRETCHED_H
 | 
				
			||||||
 | 
					#define ENEMY_WRETCHED_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "wave_survival/enemy_body.h"
 | 
				
			||||||
 | 
					#include "wave_survival/state.h"
 | 
				
			||||||
 | 
					class PatrolPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EnemyWretched : public EnemyBody {
 | 
				
			||||||
 | 
						GDCLASS(EnemyWretched, EnemyBody);
 | 
				
			||||||
 | 
						static void _bind_methods();
 | 
				
			||||||
 | 
						void ready();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						void _notification(int what);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ============================== STATES ============================== */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WretchedState : public State {
 | 
				
			||||||
 | 
						GDCLASS(WretchedState, State);
 | 
				
			||||||
 | 
						static void _bind_methods() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						virtual void set_target(Node *node) override;
 | 
				
			||||||
 | 
						EnemyWretched *get_target() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						EnemyWretched *target{ nullptr };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WretchedPatrolState : public WretchedState {
 | 
				
			||||||
 | 
						GDCLASS(WretchedPatrolState, WretchedState);
 | 
				
			||||||
 | 
						static void _bind_methods() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						virtual void enter_state() override;
 | 
				
			||||||
 | 
						virtual void process(double delta) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						NavigationAgent3D *nav{ nullptr };
 | 
				
			||||||
 | 
						int path_point{ 0 };
 | 
				
			||||||
 | 
						PatrolPath *path{ nullptr };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // !ENEMY_WRETCHED_H
 | 
				
			||||||
							
								
								
									
										58
									
								
								modules/wave_survival/enemy_body.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								modules/wave_survival/enemy_body.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,58 @@
 | 
				
			||||||
 | 
					#include "enemy_body.h"
 | 
				
			||||||
 | 
					#include "macros.h"
 | 
				
			||||||
 | 
					#include "npc_unit.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyBody::_bind_methods() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyBody::ready() {
 | 
				
			||||||
 | 
						this->fsm = cast_to<StateMachine>(get_node(NodePath("%StateMachine")));
 | 
				
			||||||
 | 
						this->nav = cast_to<NavigationAgent3D>(get_node(NodePath("%NavigationAgent3D")));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyBody::physics_process(double delta) {
 | 
				
			||||||
 | 
						GETSET(velocity, {
 | 
				
			||||||
 | 
							velocity = Vector3{ this->movement_direction.x, velocity.y, this->movement_direction.y };
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						move_and_slide();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyBody::_notification(int what) {
 | 
				
			||||||
 | 
						if (Engine::get_singleton()->is_editor_hint()) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch (what) {
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							case NOTIFICATION_READY:
 | 
				
			||||||
 | 
								set_physics_process(true);
 | 
				
			||||||
 | 
								ready();
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							case NOTIFICATION_PHYSICS_PROCESS:
 | 
				
			||||||
 | 
								physics_process(get_physics_process_delta_time());
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyBody::set_movement_direction(Vector2 direction) {
 | 
				
			||||||
 | 
						this->movement_direction = direction;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyBody::set_unit(NpcUnit *unit) {
 | 
				
			||||||
 | 
						this->unit = unit;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NpcUnit *EnemyBody::get_unit() const {
 | 
				
			||||||
 | 
						return this->unit;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NavigationAgent3D *EnemyBody::get_nav() const {
 | 
				
			||||||
 | 
						return this->nav;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EnemyBody::set_movement_speed(float value) {
 | 
				
			||||||
 | 
						this->movement_speed = value;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float EnemyBody::get_movement_speed() const {
 | 
				
			||||||
 | 
						return this->movement_speed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										36
									
								
								modules/wave_survival/enemy_body.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								modules/wave_survival/enemy_body.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					#ifndef ENEMY_BODY_H
 | 
				
			||||||
 | 
					#define ENEMY_BODY_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "scene/3d/navigation/navigation_agent_3d.h"
 | 
				
			||||||
 | 
					#include "scene/3d/physics/character_body_3d.h"
 | 
				
			||||||
 | 
					#include "wave_survival/health_status.h"
 | 
				
			||||||
 | 
					#include "wave_survival/state_machine.h"
 | 
				
			||||||
 | 
					class NpcUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EnemyBody : public CharacterBody3D {
 | 
				
			||||||
 | 
						GDCLASS(EnemyBody, CharacterBody3D);
 | 
				
			||||||
 | 
						static void _bind_methods();
 | 
				
			||||||
 | 
						void ready();
 | 
				
			||||||
 | 
						void physics_process(double delta);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						void _notification(int what);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void set_movement_direction(Vector2 direction);
 | 
				
			||||||
 | 
						void set_unit(NpcUnit *unit);
 | 
				
			||||||
 | 
						NpcUnit *get_unit() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NavigationAgent3D *get_nav() const;
 | 
				
			||||||
 | 
						void set_movement_speed(float value);
 | 
				
			||||||
 | 
						float get_movement_speed() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						float movement_speed{ 1.f };
 | 
				
			||||||
 | 
						Vector2 movement_direction{ 0, 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						StateMachine *fsm{ nullptr };
 | 
				
			||||||
 | 
						NpcUnit *unit{ nullptr };
 | 
				
			||||||
 | 
						NavigationAgent3D *nav{ nullptr };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // !ENEMY_BODY_H
 | 
				
			||||||
| 
						 | 
					@ -17,11 +17,11 @@
 | 
				
			||||||
	ADD_PROPERTY(PropertyInfo(m_type, #m_property), "set_" #m_property, \
 | 
						ADD_PROPERTY(PropertyInfo(m_type, #m_property), "set_" #m_property, \
 | 
				
			||||||
			"get_" #m_property)
 | 
								"get_" #m_property)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define GETSET(m_value, m_block)         \
 | 
					#define GETSET(m_value, ...)             \
 | 
				
			||||||
	{                                    \
 | 
						{                                    \
 | 
				
			||||||
		auto m_value{ get_##m_value() }; \
 | 
							auto m_value{ get_##m_value() }; \
 | 
				
			||||||
		m_block                          \
 | 
							__VA_ARGS__                      \
 | 
				
			||||||
				set_##m_value(m_value);  \
 | 
							set_##m_value(m_value);          \
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // !GODOT_EXTRA_MACROS_H
 | 
					#endif // !GODOT_EXTRA_MACROS_H
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										52
									
								
								modules/wave_survival/npc_unit.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								modules/wave_survival/npc_unit.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,52 @@
 | 
				
			||||||
 | 
					#include "npc_unit.h"
 | 
				
			||||||
 | 
					#include "enemy_body.h"
 | 
				
			||||||
 | 
					#include "macros.h"
 | 
				
			||||||
 | 
					#include "patrol_path.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NpcUnit::_bind_methods() {
 | 
				
			||||||
 | 
						BIND_HPROPERTY(Variant::OBJECT, patrol_path, PROPERTY_HINT_NODE_TYPE, "PatrolPath");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NpcUnit::child_added(Node *node) {
 | 
				
			||||||
 | 
						if (EnemyBody * npc{ cast_to<EnemyBody>(node) }) {
 | 
				
			||||||
 | 
							this->npcs.push_back(npc);
 | 
				
			||||||
 | 
							npc->set_unit(this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NpcUnit::enter_tree() {
 | 
				
			||||||
 | 
						this->connect("child_entered_tree", callable_mp(this, &self_type::child_added));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NpcUnit::_notification(int what) {
 | 
				
			||||||
 | 
						if (Engine::get_singleton()->is_editor_hint()) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch (what) {
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							case NOTIFICATION_ENTER_TREE:
 | 
				
			||||||
 | 
								enter_tree();
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NpcUnit::set_patrol_path(PatrolPath *path) {
 | 
				
			||||||
 | 
						this->patrol_path = path;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PatrolPath *NpcUnit::get_patrol_path() const {
 | 
				
			||||||
 | 
						return this->patrol_path;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool NpcUnit::is_aware_of_player() const {
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NpcUnit::set_patrol_speed(float speed) {
 | 
				
			||||||
 | 
						this->patrol_speed = speed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float NpcUnit::get_patrol_speed() const {
 | 
				
			||||||
 | 
						return this->patrol_speed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								modules/wave_survival/npc_unit.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								modules/wave_survival/npc_unit.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					#ifndef NPC_UNIT_H
 | 
				
			||||||
 | 
					#define NPC_UNIT_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "scene/main/node.h"
 | 
				
			||||||
 | 
					class StateMachine;
 | 
				
			||||||
 | 
					class EnemyBody;
 | 
				
			||||||
 | 
					class PatrolPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NpcUnit : public Node {
 | 
				
			||||||
 | 
						GDCLASS(NpcUnit, Node);
 | 
				
			||||||
 | 
						static void _bind_methods();
 | 
				
			||||||
 | 
						void child_added(Node *node);
 | 
				
			||||||
 | 
						void enter_tree();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						void _notification(int what);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						void set_patrol_path(PatrolPath *path);
 | 
				
			||||||
 | 
						PatrolPath *get_patrol_path() const;
 | 
				
			||||||
 | 
						bool is_aware_of_player() const; //!< becomes true when any individual in the unit sees the player.
 | 
				
			||||||
 | 
						void set_patrol_speed(float value);
 | 
				
			||||||
 | 
						float get_patrol_speed() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						Vector<EnemyBody *> npcs{};
 | 
				
			||||||
 | 
						PatrolPath *patrol_path{ nullptr };
 | 
				
			||||||
 | 
						float patrol_speed{ 3.f };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // !NPC_UNIT_H
 | 
				
			||||||
							
								
								
									
										64
									
								
								modules/wave_survival/patrol_path.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								modules/wave_survival/patrol_path.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					#include "patrol_path.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PatrolPath::_bind_methods() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PatrolPath::child_added(Node *child) {
 | 
				
			||||||
 | 
						if (Node3D * child3d{ cast_to<Node3D>(child) }) {
 | 
				
			||||||
 | 
							this->path.push_back(child3d);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PatrolPath::child_removed(Node *child) {
 | 
				
			||||||
 | 
						if (Node3D * child3d{ cast_to<Node3D>(child) }) {
 | 
				
			||||||
 | 
							this->path.erase(child3d);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PatrolPath::enter_tree() {
 | 
				
			||||||
 | 
						this->connect("child_entered_tree", callable_mp(this, &self_type::child_added));
 | 
				
			||||||
 | 
						this->connect("child_exiting_tree", callable_mp(this, &self_type::child_removed));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PatrolPath::_notification(int what) {
 | 
				
			||||||
 | 
						if (Engine::get_singleton()->is_editor_hint()) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch (what) {
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							case NOTIFICATION_ENTER_TREE:
 | 
				
			||||||
 | 
								enter_tree();
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int PatrolPath::point_count() const {
 | 
				
			||||||
 | 
						return this->path.size();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Vector3 PatrolPath::point_at(int &index) const {
 | 
				
			||||||
 | 
						index = Math::abs(index) % point_count();
 | 
				
			||||||
 | 
						return point_at_unchecked(index);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Vector3 PatrolPath::point_at_unchecked(int const index) const {
 | 
				
			||||||
 | 
						return this->path.get(index)->get_global_position();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int PatrolPath::get_closest_point(Vector3 global_position) {
 | 
				
			||||||
 | 
						int best_choice_idx{ -1 };
 | 
				
			||||||
 | 
						float best_choice_distance{ Math::INF };
 | 
				
			||||||
 | 
						for (int i{ 0 }; i < point_count(); ++i) {
 | 
				
			||||||
 | 
							int const next_idx{ (i + 1) % point_count() };
 | 
				
			||||||
 | 
							Vector3 const current{ point_at(i) };
 | 
				
			||||||
 | 
							Vector3 const next{ point_at_unchecked(next_idx) };
 | 
				
			||||||
 | 
							Vector3 const path_direction{ (next - current).normalized() };
 | 
				
			||||||
 | 
							Vector3 const direction{ (global_position - current).normalized() };
 | 
				
			||||||
 | 
							float const distance{ global_position.distance_squared_to(next) };
 | 
				
			||||||
 | 
							if (path_direction.dot(direction) > 1.0 && distance < best_choice_distance) {
 | 
				
			||||||
 | 
								best_choice_idx = next_idx;
 | 
				
			||||||
 | 
								best_choice_distance = distance;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return best_choice_idx;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								modules/wave_survival/patrol_path.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								modules/wave_survival/patrol_path.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					#ifndef PATROL_PATH_H
 | 
				
			||||||
 | 
					#define PATROL_PATH_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "scene/3d/node_3d.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PatrolPath : public Node3D {
 | 
				
			||||||
 | 
						GDCLASS(PatrolPath, Node3D);
 | 
				
			||||||
 | 
						static void _bind_methods();
 | 
				
			||||||
 | 
						void child_added(Node *child);
 | 
				
			||||||
 | 
						void child_removed(Node *child);
 | 
				
			||||||
 | 
						void enter_tree();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						void _notification(int what);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						int point_count() const;
 | 
				
			||||||
 | 
						Vector3 point_at(int &index) const;
 | 
				
			||||||
 | 
						Vector3 point_at_unchecked(int const index) const;
 | 
				
			||||||
 | 
						int get_closest_point(Vector3 global_position);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						Vector<Node3D *> path{};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // !PATROL_PATH_H
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,12 @@
 | 
				
			||||||
#include "register_types.h"
 | 
					#include "register_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "core/object/class_db.h"
 | 
					#include "core/object/class_db.h"
 | 
				
			||||||
 | 
					#include "wave_survival/enemies/enemy_wretched.h"
 | 
				
			||||||
 | 
					#include "wave_survival/enemy_body.h"
 | 
				
			||||||
#include "wave_survival/health_status.h"
 | 
					#include "wave_survival/health_status.h"
 | 
				
			||||||
#include "wave_survival/hitscan_muzzle.h"
 | 
					#include "wave_survival/hitscan_muzzle.h"
 | 
				
			||||||
 | 
					#include "wave_survival/npc_unit.h"
 | 
				
			||||||
 | 
					#include "wave_survival/patrol_path.h"
 | 
				
			||||||
#include "wave_survival/player_body.h"
 | 
					#include "wave_survival/player_body.h"
 | 
				
			||||||
#include "wave_survival/player_camera.h"
 | 
					#include "wave_survival/player_camera.h"
 | 
				
			||||||
#include "wave_survival/player_input.h"
 | 
					#include "wave_survival/player_input.h"
 | 
				
			||||||
| 
						 | 
					@ -26,6 +30,10 @@ void initialize_wave_survival_module(ModuleInitializationLevel p_level) {
 | 
				
			||||||
	GDREGISTER_CLASS(HealthStatus);
 | 
						GDREGISTER_CLASS(HealthStatus);
 | 
				
			||||||
	GDREGISTER_CLASS(StateMachine);
 | 
						GDREGISTER_CLASS(StateMachine);
 | 
				
			||||||
	GDREGISTER_CLASS(State);
 | 
						GDREGISTER_CLASS(State);
 | 
				
			||||||
 | 
						GDREGISTER_CLASS(EnemyBody);
 | 
				
			||||||
 | 
						GDREGISTER_CLASS(PatrolPath);
 | 
				
			||||||
 | 
						GDREGISTER_CLASS(NpcUnit);
 | 
				
			||||||
 | 
						GDREGISTER_CLASS(EnemyWretched);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void uninitialize_wave_survival_module(ModuleInitializationLevel p_level) {
 | 
					void uninitialize_wave_survival_module(ModuleInitializationLevel p_level) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,21 +3,12 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void StateMachine::_bind_methods() {}
 | 
					void StateMachine::_bind_methods() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void StateMachine::add_state(State *state) {
 | 
					 | 
				
			||||||
	state->set_state_machine(this);
 | 
					 | 
				
			||||||
	state->set_target(this->get_parent());
 | 
					 | 
				
			||||||
	this->states.insert(state->get_class(), state);
 | 
					 | 
				
			||||||
	if (this->current_state == nullptr) {
 | 
					 | 
				
			||||||
		this->switch_to_state(state);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void StateMachine::switch_to_state(State *state) {
 | 
					void StateMachine::switch_to_state(State *state) {
 | 
				
			||||||
	if (this->current_state) {
 | 
						if (this->current_state != nullptr) {
 | 
				
			||||||
		this->current_state->exit_state();
 | 
							this->current_state->exit_state();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this->current_state = state;
 | 
						this->current_state = state;
 | 
				
			||||||
	if (this->current_state) {
 | 
						if (this->current_state != nullptr) {
 | 
				
			||||||
		this->current_state->enter_state();
 | 
							this->current_state->enter_state();
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		print_error("StateMachine::switch_to_state: New state is nullptr, StateMachine will now stop working");
 | 
							print_error("StateMachine::switch_to_state: New state is nullptr, StateMachine will now stop working");
 | 
				
			||||||
| 
						 | 
					@ -53,3 +44,18 @@ void StateMachine::_notification(int what) {
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					StateMachine::~StateMachine() {
 | 
				
			||||||
 | 
						for (KeyValue<StringName, State *> kvp : this->states) {
 | 
				
			||||||
 | 
							memdelete(kvp.value);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void StateMachine::add_state(State *state) {
 | 
				
			||||||
 | 
						state->set_state_machine(this);
 | 
				
			||||||
 | 
						state->set_target(this->get_parent());
 | 
				
			||||||
 | 
						this->states.insert(state->get_class(), state);
 | 
				
			||||||
 | 
						if (this->current_state == nullptr) {
 | 
				
			||||||
 | 
							this->switch_to_state(state);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,6 @@ class State;
 | 
				
			||||||
class StateMachine : public Node {
 | 
					class StateMachine : public Node {
 | 
				
			||||||
	GDCLASS(StateMachine, Node);
 | 
						GDCLASS(StateMachine, Node);
 | 
				
			||||||
	static void _bind_methods();
 | 
						static void _bind_methods();
 | 
				
			||||||
	void add_state(State *state);
 | 
					 | 
				
			||||||
	void switch_to_state(State *state);
 | 
						void switch_to_state(State *state);
 | 
				
			||||||
	void ready();
 | 
						void ready();
 | 
				
			||||||
	void process(double delta);
 | 
						void process(double delta);
 | 
				
			||||||
| 
						 | 
					@ -15,6 +14,10 @@ class StateMachine : public Node {
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
	void _notification(int what);
 | 
						void _notification(int what);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						~StateMachine();
 | 
				
			||||||
 | 
						void add_state(State *state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	State *current_state{ nullptr };
 | 
						State *current_state{ nullptr };
 | 
				
			||||||
	HashMap<StringName, State *> states{};
 | 
						HashMap<StringName, State *> states{};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										21
									
								
								project/objects/enemies/enemy_wretched.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								project/objects/enemies/enemy_wretched.tscn
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					[gd_scene load_steps=3 format=3 uid="uid://dqlqgk1veyos8"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ext_resource type="PackedScene" uid="uid://7esw31b76x4" path="res://assets/models/base_character.blend" id="1_qot2n"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ng1ul"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="EnemyWretched" type="EnemyWretched"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="base_character" parent="." instance=ExtResource("1_qot2n")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
 | 
				
			||||||
 | 
					transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
 | 
				
			||||||
 | 
					shape = SubResource("CapsuleShape3D_ng1ul")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="StateMachine" type="StateMachine" parent="."]
 | 
				
			||||||
 | 
					unique_name_in_owner = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
 | 
				
			||||||
 | 
					unique_name_in_owner = true
 | 
				
			||||||
 | 
					path_desired_distance = 0.25
 | 
				
			||||||
 | 
					debug_enabled = true
 | 
				
			||||||
		Loading…
	
		Reference in a new issue