Compare commits
	
		
			20 commits
		
	
	
		
			main
			...
			basic-enem
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 9172b98b9b | ||
|   | 2e4591c3df | ||
|   | 3863b622f5 | ||
|   | 5badb8a68f | ||
|   | d3e8c75d1a | ||
|   | 31b5312cf5 | ||
|   | 491e375b6e | ||
|   | 2c3b3f2724 | ||
|   | a86b912d01 | ||
|   | d5a6aec9d1 | ||
|   | 8366d5b5df | ||
|   | 71596133a5 | ||
|   | f8ffa67a11 | ||
|   | 85b0c66cb3 | ||
|   | f82365b08b | ||
|   | c55d6b67ef | ||
|   | c689d16dfd | ||
|   | ca53fb4ae1 | ||
|   | 5967bbb95b | ||
|   | 8fca1d154f | 
|  | @ -1,46 +0,0 @@ | ||||||
| [gd_scene load_steps=6 format=3 uid="uid://deb8qiasxsobt"] |  | ||||||
| 
 |  | ||||||
| [sub_resource type="SphereShape3D" id="SphereShape3D_tnc8b"] |  | ||||||
| radius = 7.23 |  | ||||||
| 
 |  | ||||||
| [sub_resource type="SphereShape3D" id="SphereShape3D_1inhn"] |  | ||||||
| 
 |  | ||||||
| [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_y3cyo"] |  | ||||||
| albedo_color = Color(0.0666667, 0.223529, 0.254902, 1) |  | ||||||
| 
 |  | ||||||
| [sub_resource type="BoxMesh" id="BoxMesh_y311e"] |  | ||||||
| material = SubResource("StandardMaterial3D_y3cyo") |  | ||||||
| size = Vector3(0.77, 0.59, 0.805) |  | ||||||
| 
 |  | ||||||
| [sub_resource type="BoxMesh" id="BoxMesh_bc6co"] |  | ||||||
| material = SubResource("StandardMaterial3D_y3cyo") |  | ||||||
| size = Vector3(0.475, 0.495, 0.47) |  | ||||||
| 
 |  | ||||||
| [node name="Enemy" type="Enemy"] |  | ||||||
| collision_layer = 7 |  | ||||||
| 
 |  | ||||||
| [node name="NavigationAgent3D" type="NavigationAgent3D" parent="."] |  | ||||||
| path_desired_distance = 2.25 |  | ||||||
| 
 |  | ||||||
| [node name="VisionArea" type="Area3D" parent="."] |  | ||||||
| collision_layer = 0 |  | ||||||
| collision_mask = 2 |  | ||||||
| 
 |  | ||||||
| [node name="CollisionShape3D" type="CollisionShape3D" parent="VisionArea"] |  | ||||||
| transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.32109, 0) |  | ||||||
| shape = SubResource("SphereShape3D_tnc8b") |  | ||||||
| 
 |  | ||||||
| [node name="CollisionShape3D" type="CollisionShape3D" parent="."] |  | ||||||
| transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.495191, 0) |  | ||||||
| shape = SubResource("SphereShape3D_1inhn") |  | ||||||
| 
 |  | ||||||
| [node name="Health" type="Health" parent="."] |  | ||||||
| max_health = 10 |  | ||||||
| 
 |  | ||||||
| [node name="MeshInstance3D" type="MeshInstance3D" parent="."] |  | ||||||
| transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.445159, -0.0811542) |  | ||||||
| mesh = SubResource("BoxMesh_y311e") |  | ||||||
| 
 |  | ||||||
| [node name="MeshInstance3D2" type="MeshInstance3D" parent="."] |  | ||||||
| transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.430494, 0.458904) |  | ||||||
| mesh = SubResource("BoxMesh_bc6co") |  | ||||||
							
								
								
									
										26
									
								
								godot/Enemies/flower.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								godot/Enemies/flower.tscn
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | [gd_scene load_steps=3 format=3 uid="uid://dopygosr0hxb5"] | ||||||
|  | 
 | ||||||
|  | [sub_resource type="SphereShape3D" id="SphereShape3D_ag2i6"] | ||||||
|  | radius = 2.63833 | ||||||
|  | 
 | ||||||
|  | [sub_resource type="CylinderMesh" id="CylinderMesh_vcf5j"] | ||||||
|  | height = 0.2 | ||||||
|  | radial_segments = 16 | ||||||
|  | rings = 1 | ||||||
|  | cap_bottom = false | ||||||
|  | 
 | ||||||
|  | [node name="CharacterActor" type="CharacterActor"] | ||||||
|  | 
 | ||||||
|  | [node name="Health" type="Health" parent="."] | ||||||
|  | 
 | ||||||
|  | [node name="Planner" type="Planner" parent="."] | ||||||
|  | 
 | ||||||
|  | [node name="CharacterAwareness" type="CharacterAwareness" parent="."] | ||||||
|  | collision_mask = 2 | ||||||
|  | 
 | ||||||
|  | [node name="CollisionShape3D" type="CollisionShape3D" parent="CharacterAwareness"] | ||||||
|  | shape = SubResource("SphereShape3D_ag2i6") | ||||||
|  | 
 | ||||||
|  | [node name="MeshInstance3D" type="MeshInstance3D" parent="."] | ||||||
|  | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.364549, 0) | ||||||
|  | mesh = SubResource("CylinderMesh_vcf5j") | ||||||
|  | @ -2,14 +2,19 @@ | ||||||
| 
 | 
 | ||||||
| [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_l2qcn"] | [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_l2qcn"] | ||||||
| albedo_color = Color(1, 0.345098, 0, 1) | albedo_color = Color(1, 0.345098, 0, 1) | ||||||
|  | emission_enabled = true | ||||||
|  | emission = Color(1, 0.683333, 0, 1) | ||||||
| 
 | 
 | ||||||
| [sub_resource type="CapsuleMesh" id="CapsuleMesh_0fa8x"] | [sub_resource type="CapsuleMesh" id="CapsuleMesh_0fa8x"] | ||||||
| material = SubResource("StandardMaterial3D_l2qcn") | material = SubResource("StandardMaterial3D_l2qcn") | ||||||
| radius = 0.02 | radius = 0.02 | ||||||
| height = 0.3 | height = 0.12 | ||||||
|  | radial_segments = 8 | ||||||
|  | rings = 2 | ||||||
| 
 | 
 | ||||||
| [node name="PelletProjectile" type="PelletProjectile"] | [node name="PelletProjectile" type="PelletProjectile"] | ||||||
| 
 | 
 | ||||||
| [node name="MeshInstance3D" type="MeshInstance3D" parent="."] | [node name="MeshInstance3D" type="MeshInstance3D" parent="."] | ||||||
| transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0) | transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0) | ||||||
|  | cast_shadow = 0 | ||||||
| mesh = SubResource("CapsuleMesh_0fa8x") | mesh = SubResource("CapsuleMesh_0fa8x") | ||||||
|  |  | ||||||
|  | @ -3,7 +3,8 @@ | ||||||
| [ext_resource type="PackedScene" uid="uid://c3fyth1hvgy2m" path="res://Projectiles/default_pellet.tscn" id="1_h12ld"] | [ext_resource type="PackedScene" uid="uid://c3fyth1hvgy2m" path="res://Projectiles/default_pellet.tscn" id="1_h12ld"] | ||||||
| 
 | 
 | ||||||
| [sub_resource type="Curve" id="Curve_tdh3d"] | [sub_resource type="Curve" id="Curve_tdh3d"] | ||||||
| _data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0] | max_value = 2.0 | ||||||
|  | _data = [Vector2(0, 2), 0.0, 0.0, 0, 0, Vector2(1, 2), 0.0, 0.0, 0, 0] | ||||||
| point_count = 2 | point_count = 2 | ||||||
| 
 | 
 | ||||||
| [resource] | [resource] | ||||||
|  |  | ||||||
|  | @ -36,7 +36,7 @@ prerequisites = { | ||||||
| 
 | 
 | ||||||
| [sub_resource type="CapsuleMesh" id="CapsuleMesh_rwcvu"] | [sub_resource type="CapsuleMesh" id="CapsuleMesh_rwcvu"] | ||||||
| radial_segments = 12 | radial_segments = 12 | ||||||
| rings = 1 | rings = 2 | ||||||
| 
 | 
 | ||||||
| [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_scmx3"] | [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_scmx3"] | ||||||
| albedo_color = Color(0.94902, 0.909804, 0, 1) | albedo_color = Color(0.94902, 0.909804, 0, 1) | ||||||
|  | @ -52,6 +52,9 @@ collision_layer = 7 | ||||||
| actions = [SubResource("Action_gtisq"), SubResource("Action_cwmvs")] | actions = [SubResource("Action_gtisq"), SubResource("Action_cwmvs")] | ||||||
| goals = [SubResource("Goal_sqtwb")] | goals = [SubResource("Goal_sqtwb")] | ||||||
| 
 | 
 | ||||||
|  | [node name="CharacterAwareness" type="CharacterAwareness" parent="."] | ||||||
|  | collision_mask = 2 | ||||||
|  | 
 | ||||||
| [node name="Health" type="Health" parent="."] | [node name="Health" type="Health" parent="."] | ||||||
| max_health = 5 | max_health = 5 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ | ||||||
| #include "tunnels_game_mode.hpp" | #include "tunnels_game_mode.hpp" | ||||||
| #include "utils/game_root.hpp" | #include "utils/game_root.hpp" | ||||||
| #include "utils/godot_macros.h" | #include "utils/godot_macros.h" | ||||||
| #include <cmath> |  | ||||||
| #include <godot_cpp/classes/navigation_agent3d.hpp> | #include <godot_cpp/classes/navigation_agent3d.hpp> | ||||||
| #include <godot_cpp/classes/time.hpp> | #include <godot_cpp/classes/time.hpp> | ||||||
| #include <godot_cpp/variant/callable.hpp> | #include <godot_cpp/variant/callable.hpp> | ||||||
|  | @ -16,11 +15,12 @@ namespace godot { | ||||||
| void CharacterActor::_bind_methods() { | void CharacterActor::_bind_methods() { | ||||||
| #define CLASSNAME CharacterActor | #define CLASSNAME CharacterActor | ||||||
|     GDPROPERTY_HINTED(rotation_speed_curve, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Curve"); |     GDPROPERTY_HINTED(rotation_speed_curve, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Curve"); | ||||||
|     GDPROPERTY_HINTED(target, Variant::OBJECT, PROPERTY_HINT_NODE_TYPE, "Node"); |     GDPROPERTY_HINTED(character_type, Variant::INT, PROPERTY_HINT_ENUM, "Neutral,Player,Enemy"); | ||||||
|     GDPROPERTY(acceleration, Variant::FLOAT); |     GDPROPERTY(acceleration, Variant::FLOAT); | ||||||
|     GDPROPERTY(walk_speed, Variant::FLOAT); |     GDPROPERTY(walk_speed, Variant::FLOAT); | ||||||
|     GDPROPERTY(sprint_speed, Variant::FLOAT); |     GDPROPERTY(sprint_speed, Variant::FLOAT); | ||||||
|     GDPROPERTY(rotation_speed, Variant::FLOAT); |     GDPROPERTY(rotation_speed, Variant::FLOAT); | ||||||
|  |     GDFUNCTION(get_target); | ||||||
|     GDFUNCTION(get_is_near_player); |     GDFUNCTION(get_is_near_player); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -31,8 +31,7 @@ void CharacterActor::_enter_tree() { GDGAMEONLY(); | ||||||
|     this->health = this->get_node<Health>("Health"); |     this->health = this->get_node<Health>("Health"); | ||||||
|     this->primary_weapon_pool = this->get_node<ProjectilePool>("ProjectilePool"); |     this->primary_weapon_pool = this->get_node<ProjectilePool>("ProjectilePool"); | ||||||
|     this->planner = this->get_node<goap::Planner>("Planner"); |     this->planner = this->get_node<goap::Planner>("Planner"); | ||||||
|     Ref<TunnelsGameMode> game_mode = GameRoot::get_singleton()->get_game_mode(); |     Ref<TunnelsGameMode>(GameRoot3D::get_singleton()->get_game_mode())->register_player_character(this); | ||||||
|     game_mode->register_player_character(this); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CharacterActor::_process(double delta_time) { GDGAMEONLY(); | void CharacterActor::_process(double delta_time) { GDGAMEONLY(); | ||||||
|  | @ -63,9 +62,13 @@ void CharacterActor::move(Vector3 world_vector) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CharacterActor::aim(Vector3 at) { | void CharacterActor::aim(Vector3 at) { | ||||||
|     // calculate the forward vector by normalized difference between player character and the target on the XZ plane
 |     // the forward vector by normalized difference between player character and the target on the XZ plane
 | ||||||
|     Vector3 const position{this->weapon_muzzle->get_global_position()}; |     Vector3 const pos_flat{this->get_global_position().x, 0.f, this->get_global_position().z}; | ||||||
|     Vector3 const forward{Vector3{at.x, 0.f, at.z} - Vector3{position.x, 0.f, position.z}}; |     Vector3 const target_flat{at.x, 0.f, at.z}; | ||||||
|  |     Vector3 const muzzle_flat{this->weapon_muzzle->get_global_position().x, 0.f, this->weapon_muzzle->get_global_position().z}; | ||||||
|  |     if(pos_flat.distance_squared_to(target_flat) < pos_flat.distance_squared_to(muzzle_flat)) | ||||||
|  |         return; | ||||||
|  |     Vector3 const forward{target_flat - muzzle_flat}; | ||||||
|     this->aim_direction(forward.normalized()); |     this->aim_direction(forward.normalized()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -101,7 +104,6 @@ void CharacterActor::set_firing(bool firing) { | ||||||
| void CharacterActor::set_manual_mode(bool value) { | void CharacterActor::set_manual_mode(bool value) { | ||||||
|     this->mode_manual = value; |     this->mode_manual = value; | ||||||
|     ProcessMode const mode = value ?  ProcessMode::PROCESS_MODE_DISABLED : ProcessMode::PROCESS_MODE_PAUSABLE; |     ProcessMode const mode = value ?  ProcessMode::PROCESS_MODE_DISABLED : ProcessMode::PROCESS_MODE_PAUSABLE; | ||||||
|     //this->nav_agent->set_process_mode(mode);
 |  | ||||||
|     this->nav_agent->set_avoidance_priority(value ? 1.f : 0.9f); |     this->nav_agent->set_avoidance_priority(value ? 1.f : 0.9f); | ||||||
|     this->set_state(goap::State::new_invalid()); |     this->set_state(goap::State::new_invalid()); | ||||||
| } | } | ||||||
|  | @ -141,7 +143,7 @@ Vector3 CharacterActor::get_velocity_target() const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CharacterActor::get_is_near_player() const { | bool CharacterActor::get_is_near_player() const { | ||||||
|     return Ref<TunnelsGameMode>(GameRoot::get_singleton()->get_game_mode()) |     return Ref<TunnelsGameMode>(GameRoot3D::get_singleton()->get_game_mode()) | ||||||
|         ->get_player_instance() |         ->get_player_instance() | ||||||
|         ->get_character() |         ->get_character() | ||||||
|         ->get_global_position().distance_to(this->get_global_position()) < 5.f; |         ->get_global_position().distance_to(this->get_global_position()) < 5.f; | ||||||
|  | @ -152,7 +154,8 @@ bool CharacterActor::get_is_near_target() const { | ||||||
|     Node3D *target_node3d = Object::cast_to<Node3D>(this->target); |     Node3D *target_node3d = Object::cast_to<Node3D>(this->target); | ||||||
|     return target_marker |     return target_marker | ||||||
|         ? target_marker->is_point_on(this->get_global_position()) |         ? target_marker->is_point_on(this->get_global_position()) | ||||||
|         : (target_node3d && target_node3d->get_global_position().distance_to(this->get_global_position()) < 5.f); |         : (target_node3d | ||||||
|  |                 && target_node3d->get_global_position().distance_to(this->get_global_position()) < 5.f); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Vector3 CharacterActor::get_move_target() const { | Vector3 CharacterActor::get_move_target() const { | ||||||
|  | @ -195,64 +198,13 @@ void CharacterActor::set_state(goap::State state) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CharacterActor::process_behaviour(double delta_time) { | void CharacterActor::set_character_type(int character_type) { | ||||||
|     if(this->current_state.is_complete(this) || this->planner->is_action_complete()) |     this->character_type = static_cast<CharacterType>(character_type); | ||||||
|         this->set_state(this->planner->get_next_state()); |     UtilityFunctions::print("set_character_type ", character_type); | ||||||
|     switch(this->current_state.type) { |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     case goap::State::STATE_MOVE_TO: |  | ||||||
|         if(this->nav_agent->get_target_position().distance_to(this->get_move_target()) > 2.f) |  | ||||||
|             this->nav_agent->set_target_position(this->get_move_target()); |  | ||||||
|         break; |  | ||||||
|     case goap::State::STATE_ACTIVATE: |  | ||||||
|         break; |  | ||||||
|     case goap::State::STATE_ANIMATE: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CharacterActor::process_navigation(double delta_time) { | int CharacterActor::get_character_type() const { | ||||||
|     float const distance_sqr = this->nav_agent->get_target_position().distance_squared_to(this->get_global_position()); |     return static_cast<int>(this->character_type); | ||||||
|     float const distance_target_sqr = std::pow(this->nav_agent->get_target_desired_distance(), 2.f); |  | ||||||
|     if(!this->nav_agent->is_navigation_finished() && distance_sqr >= distance_target_sqr) { |  | ||||||
|         Vector3 const target_position = this->nav_agent->get_next_path_position(); |  | ||||||
|         Vector3 const direction = (target_position - this->get_global_position()).normalized(); |  | ||||||
|         if(this->nav_agent->get_avoidance_enabled()) |  | ||||||
|             this->nav_agent->set_velocity(direction * CharacterActor::walk_speed); |  | ||||||
|         else |  | ||||||
|             this->move(direction); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CharacterActor::process_rotation(double delta_time) { |  | ||||||
|     // copy the current transform and basis matrix
 |  | ||||||
|     Transform3D trans{this->get_global_transform()}; |  | ||||||
|     Basis basis = trans.get_basis(); |  | ||||||
|     // construct the current rotation ..
 |  | ||||||
|     Quaternion const current_quaternion = basis.get_rotation_quaternion(); |  | ||||||
|     // .. and the target rotation from their respective bases
 |  | ||||||
|     Quaternion const target_quaternion = this->target_rotation.get_rotation_quaternion(); |  | ||||||
|     // calculate the angle that still needs to be traveled
 |  | ||||||
|     float const angle = current_quaternion.angle_to(target_quaternion); |  | ||||||
|     // calculate the angle amount that can be moved this frame
 |  | ||||||
|     float const angle_step{float(this->rotation_speed_curve->sample(angle) * CharacterActor::rotation_speed * delta_time)}; |  | ||||||
|     // update this object's global transform with the new rotation
 |  | ||||||
|     basis.set_quaternion(angle < angle_step ? target_quaternion // to avoid overshooting, check if the max step is smaller than the angle distance
 |  | ||||||
|                          : current_quaternion.slerp(target_quaternion, angle_step / angle)); // convert the angle step to a lerp t value between current and target rotations
 |  | ||||||
|     trans.set_basis(basis); |  | ||||||
|     this->set_global_transform(trans); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CharacterActor::try_fire_weapon() { |  | ||||||
|     if(float(Time::get_singleton()->get_ticks_msec()) / 1000.f < this->fire_timer) |  | ||||||
|         return; |  | ||||||
|     if(!this->data->get_weapon()->get_allow_automatic()) |  | ||||||
|         this->set_firing(false); |  | ||||||
|     this->fire_timer = float(Time::get_singleton()->get_ticks_msec()) / 1000.f + this->fire_interval; |  | ||||||
|     Node3D *node = this->primary_weapon_pool->claim_projectile(); |  | ||||||
|     if(node != nullptr) |  | ||||||
|         node->set_global_transform(this->weapon_muzzle->get_global_transform()); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CharacterActor::set_acceleration(float acceleration) { | void CharacterActor::set_acceleration(float acceleration) { | ||||||
|  | @ -286,4 +238,59 @@ void CharacterActor::set_rotation_speed(float rotation_speed) { | ||||||
| float CharacterActor::get_rotation_speed() const { | float CharacterActor::get_rotation_speed() const { | ||||||
|     return this->rotation_speed; |     return this->rotation_speed; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void CharacterActor::process_behaviour(double delta_time) { | ||||||
|  |     if(this->current_state.is_complete(this) || this->planner->is_action_complete()) | ||||||
|  |         this->set_state(this->planner->get_next_state()); | ||||||
|  |     switch(this->current_state.type) { | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     case goap::State::STATE_MOVE_TO: | ||||||
|  |         if(this->nav_agent->get_target_position().distance_to(this->get_move_target()) > 2.f) | ||||||
|  |             this->nav_agent->set_target_position(this->get_move_target()); | ||||||
|  |         break; | ||||||
|  |     case goap::State::STATE_ACTIVATE: | ||||||
|  |         break; | ||||||
|  |     case goap::State::STATE_ANIMATE: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CharacterActor::process_navigation(double delta_time) { | ||||||
|  |     float const sqr_dist = this->nav_agent->get_target_position().distance_squared_to(this->get_global_position()); | ||||||
|  |     float const sqr_dist_target = Math::pow(this->nav_agent->get_target_desired_distance(), 2.0); | ||||||
|  |     if(this->nav_agent->is_navigation_finished() || sqr_dist < sqr_dist_target) | ||||||
|  |         return; | ||||||
|  |     Vector3 const target_pos = this->nav_agent->get_next_path_position(); | ||||||
|  |     Vector3 const direction = (target_pos - this->get_global_position()).normalized(); | ||||||
|  |     if(this->nav_agent->get_avoidance_enabled()) | ||||||
|  |         this->nav_agent->set_velocity(direction * CharacterActor::walk_speed); | ||||||
|  |     else | ||||||
|  |         this->move(direction); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CharacterActor::process_rotation(double delta_time) { | ||||||
|  |     Basis basis = this->get_global_basis(); | ||||||
|  |     Quaternion const current_quaternion = basis.get_rotation_quaternion(); | ||||||
|  |     Quaternion const target_quaternion = this->target_rotation.get_rotation_quaternion(); | ||||||
|  |     // the angle that still needs to be traveled
 | ||||||
|  |     float const angle = current_quaternion.angle_to(target_quaternion); | ||||||
|  |     // calculate the angle amount that can be moved this frame
 | ||||||
|  |     float const step{float(this->rotation_speed_curve->sample(angle) * CharacterActor::rotation_speed * delta_time)}; | ||||||
|  |     // update this object's global transform with the new rotation
 | ||||||
|  |     basis.set_quaternion(angle < step ? target_quaternion // to avoid overshooting, check if the max step is smaller than the angle distance
 | ||||||
|  |                          : current_quaternion.slerp(target_quaternion, step / angle)); // convert the angle step to a lerp t value between current and target rotations
 | ||||||
|  |     this->set_global_basis(basis); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CharacterActor::try_fire_weapon() { | ||||||
|  |     if(float(Time::get_singleton()->get_ticks_msec()) / 1000.f < this->fire_timer) | ||||||
|  |         return; | ||||||
|  |     if(!this->data->get_weapon()->get_allow_automatic()) | ||||||
|  |         this->set_firing(false); | ||||||
|  |     this->fire_timer = float(Time::get_singleton()->get_ticks_msec()) / 1000.f + this->fire_interval; | ||||||
|  |     Node3D *node = this->primary_weapon_pool->claim_projectile(); | ||||||
|  |     if(node != nullptr) | ||||||
|  |         node->set_global_transform(this->weapon_muzzle->get_global_transform()); | ||||||
|  | } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| #ifndef PLAYER_CHARACTER_HPP | #ifndef CHARACTER_ACTOR_HPP | ||||||
| #define PLAYER_CHARACTER_HPP | #define CHARACTER_ACTOR_HPP | ||||||
| 
 | 
 | ||||||
| #include "character_data.hpp" | #include "character_data.hpp" | ||||||
| #include "health.hpp" | #include "health.hpp" | ||||||
|  | @ -17,10 +17,10 @@ namespace goap { | ||||||
|     class Planner; |     class Planner; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum class Team { | enum class CharacterType : unsigned { | ||||||
|     CHARACTER_TEAM_WORLD = 0u, |     NEUTRAL, | ||||||
|     CHARACTER_TEAM_PLAYER = 1u, |     PLAYER, | ||||||
|     CHARACTER_TEAM_ENEMY = 2u, |     PLANT | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class CharacterActor : public CharacterBody3D, | class CharacterActor : public CharacterBody3D, | ||||||
|  | @ -70,7 +70,9 @@ public: | ||||||
|     Node *get_target() const; |     Node *get_target() const; | ||||||
|     goap::Planner *get_planner() const; |     goap::Planner *get_planner() const; | ||||||
|     void set_state(goap::State state); |     void set_state(goap::State state); | ||||||
| 
 |     void set_character_type(int character_type); | ||||||
|  |     int get_character_type() const; | ||||||
|  |     // character settings
 | ||||||
|     void set_acceleration(float acceleration); |     void set_acceleration(float acceleration); | ||||||
|     float get_acceleration() const; |     float get_acceleration() const; | ||||||
|     void set_walk_speed(float walk_speed); |     void set_walk_speed(float walk_speed); | ||||||
|  | @ -113,6 +115,8 @@ private: | ||||||
|     Ref<CharacterData> data; |     Ref<CharacterData> data; | ||||||
|     float fire_interval{0.f}; // derived from 1 / the current weapon's rps
 |     float fire_interval{0.f}; // derived from 1 / the current weapon's rps
 | ||||||
| 
 | 
 | ||||||
|  |     CharacterType character_type{CharacterType::NEUTRAL}; | ||||||
|  | 
 | ||||||
|     float acceleration{20.f}; |     float acceleration{20.f}; | ||||||
|     float walk_speed{3.f}; |     float walk_speed{3.f}; | ||||||
|     float sprint_speed{5.f}; |     float sprint_speed{5.f}; | ||||||
|  | @ -120,4 +124,4 @@ private: | ||||||
| }; | }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif // !PLAYER_CHARACTER_HPP
 | #endif // !CHARACTER_ACTOR_HPP
 | ||||||
|  |  | ||||||
							
								
								
									
										43
									
								
								src/character_awareness.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/character_awareness.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | ||||||
|  | #include "character_awareness.hpp" | ||||||
|  | #include "character_actor.hpp" | ||||||
|  | #include "utils/godot_macros.h" | ||||||
|  | #include <godot_cpp/variant/callable_method_pointer.hpp> | ||||||
|  | 
 | ||||||
|  | namespace godot { | ||||||
|  | void CharacterAwareness::_bind_methods() { | ||||||
|  | #define CLASSNAME CharacterAwareness | ||||||
|  |     GDSIGNAL("detect_actor", PropertyInfo(Variant::OBJECT, "character", PROPERTY_HINT_NODE_TYPE, "CharacterActor")); | ||||||
|  |     GDSIGNAL("lose_actor", PropertyInfo(Variant::OBJECT, "character", PROPERTY_HINT_NODE_TYPE, "CharacterActor")); | ||||||
|  |     GDSIGNAL("detect_node", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NODE_TYPE, "Node")); | ||||||
|  |     GDSIGNAL("lose_node", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NODE_TYPE, "Node")); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CharacterAwareness::_enter_tree() { | ||||||
|  |     this->set_collision_mask(0x2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CharacterAwareness::_ready() { | ||||||
|  |     this->connect("body_entered", callable_mp(this, &CharacterAwareness::on_body_entered)); | ||||||
|  |     this->connect("body_exited", callable_mp(this, &CharacterAwareness::on_body_exited)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CharacterAwareness::on_body_entered(Node *node) { | ||||||
|  |     this->node_awareness.insert(node); | ||||||
|  |     this->emit_signal("detect_node", node); | ||||||
|  |     CharacterActor *actor = Object::cast_to<CharacterActor>(node); | ||||||
|  |     if(actor) { | ||||||
|  |         this->actor_awareness.insert(actor); | ||||||
|  |         this->emit_signal("detect_actor", actor); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CharacterAwareness::on_body_exited(Node *node) { | ||||||
|  |     this->node_awareness.erase(node); | ||||||
|  |     this->emit_signal("lose_node", node); | ||||||
|  |     CharacterActor *actor = Object::cast_to<CharacterActor>(node); | ||||||
|  |     if(actor) { | ||||||
|  |         this->actor_awareness.erase(actor); | ||||||
|  |         this->emit_signal("lose_actor", actor); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								src/character_awareness.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/character_awareness.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | #ifndef CHARACTER_AWARENESS_HPP | ||||||
|  | #define CHARACTER_AWARENESS_HPP | ||||||
|  | 
 | ||||||
|  | #include "character_actor.hpp" | ||||||
|  | #include <godot_cpp/classes/area3d.hpp> | ||||||
|  | #include <godot_cpp/templates/hash_set.hpp> | ||||||
|  | 
 | ||||||
|  | namespace godot { | ||||||
|  | class CharacterAwareness : public Area3D { | ||||||
|  |     GDCLASS(CharacterAwareness, Area3D); | ||||||
|  |     static void _bind_methods(); | ||||||
|  | public: | ||||||
|  |     virtual void _enter_tree() override; | ||||||
|  |     virtual void _ready() override; | ||||||
|  |     void on_body_entered(Node *node); | ||||||
|  |     void on_body_exited(Node *node); | ||||||
|  | private: | ||||||
|  |     // awareness of characters specifically
 | ||||||
|  |     HashSet<CharacterActor*> actor_awareness{}; | ||||||
|  |     // not a character :( cringe
 | ||||||
|  |     HashSet<Node*> node_awareness{}; | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // !CHARACTER_AWARENESS_HPP
 | ||||||
|  | @ -1,79 +0,0 @@ | ||||||
| #include "enemy.hpp" |  | ||||||
| #include "godot_cpp/classes/time.hpp" |  | ||||||
| #include "health.hpp" |  | ||||||
| #include "character_actor.hpp" |  | ||||||
| #include "utils/godot_macros.h" |  | ||||||
| #include <godot_cpp/classes/navigation_agent3d.hpp> |  | ||||||
| 
 |  | ||||||
| namespace godot { |  | ||||||
| void Enemy::_bind_methods() { |  | ||||||
| #define CLASSNAME Enemy |  | ||||||
|     GDFUNCTION_ARGS(body_entered_vision_area, "body"); |  | ||||||
|     GDFUNCTION_ARGS(on_death, "damage"); |  | ||||||
|     GDFUNCTION_ARGS(on_damage, "damage", "remaining"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Enemy::_ready() { GDGAMEONLY(); |  | ||||||
|     this->health = this->get_node<Health>("Health"); |  | ||||||
|     this->nav_agent = this->get_node<NavigationAgent3D>("NavigationAgent3D"); |  | ||||||
|     this->vision_area = this->get_node<Area3D>("VisionArea"); |  | ||||||
|     this->vision_area->connect("body_entered", Callable(this, "body_entered_vision_area")); |  | ||||||
|     this->update_navigation_target(); |  | ||||||
|     this->health->connect("death", Callable(this, "on_death")); |  | ||||||
|     this->health->connect("damage", Callable(this, "on_damage")); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Enemy::_process(double delta_time) { GDGAMEONLY(); |  | ||||||
|     if(this->renav_time > Time::get_singleton()->get_ticks_msec() / 1000.f) { |  | ||||||
|         this->update_navigation_target(); |  | ||||||
|     } |  | ||||||
|     this->process_navigation(delta_time); |  | ||||||
|     this->move_and_slide(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Enemy::process_navigation(double delta_time) { |  | ||||||
|     if(this->nav_agent->is_navigation_finished()) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     Vector3 const desired_direction = (this->nav_agent->get_next_path_position() - this->get_global_position()).normalized(); |  | ||||||
|     this->set_velocity(desired_direction); |  | ||||||
|     Transform3D trans = this->get_global_transform(); |  | ||||||
|     Vector3 const forward = desired_direction; |  | ||||||
|     trans.set_basis(Basis{desired_direction.cross(Vector3{0.f, 1.f, 0.f}), Vector3{0.f, 1.f, 0.f}, forward}); |  | ||||||
|     this->set_global_transform(trans); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Enemy::body_entered_vision_area(Node3D *body) { |  | ||||||
|     CharacterActor *player = Object::cast_to<CharacterActor>(body); |  | ||||||
|     if(player == nullptr) |  | ||||||
|         return; |  | ||||||
|     // TODO: replace this with some condition deciding wether to attack the new character or the current target
 |  | ||||||
|     this->target_player = player; |  | ||||||
|     this->update_navigation_target(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Enemy::update_navigation_target() { |  | ||||||
|     this->renav_time = float(Time::get_singleton()->get_ticks_msec()) / 1000.f + Enemy::RENAV_INTERVAL; |  | ||||||
|     if(this->target_player == nullptr) |  | ||||||
|         this->nav_agent->set_target_position(this->get_global_position()); |  | ||||||
|     else |  | ||||||
|         this->nav_agent->set_target_position(this->target_player->get_global_position()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Enemy::on_damage(int delta, int health_left) { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Enemy::on_death(int delta) { |  | ||||||
|     this->queue_free(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Health *Enemy::get_health() { |  | ||||||
|     return this->health; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Health const *Enemy::get_health() const { |  | ||||||
|     return this->health; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| float const Enemy::RENAV_INTERVAL{0.25f}; |  | ||||||
| } |  | ||||||
|  | @ -1,43 +0,0 @@ | ||||||
| #ifndef ENEMY_HPP |  | ||||||
| #define ENEMY_HPP |  | ||||||
| 
 |  | ||||||
| #include "godot_cpp/classes/area3d.hpp" |  | ||||||
| #include "health.hpp" |  | ||||||
| #include <godot_cpp/classes/character_body3d.hpp> |  | ||||||
| 
 |  | ||||||
| namespace godot { |  | ||||||
| class CharacterActor; |  | ||||||
| class NavigationAgent3D; |  | ||||||
| 
 |  | ||||||
| class Enemy : public CharacterBody3D, |  | ||||||
|               public IHealthEntity { |  | ||||||
|     GDCLASS(Enemy, CharacterBody3D); |  | ||||||
|     static void _bind_methods(); |  | ||||||
| public: |  | ||||||
|     virtual void _ready() override; |  | ||||||
|     virtual void _process(double delta_time) override; |  | ||||||
|     void process_navigation(double delta_time); |  | ||||||
| 
 |  | ||||||
|     void body_entered_vision_area(Node3D *body); |  | ||||||
| 
 |  | ||||||
|     void update_navigation_target(); |  | ||||||
| 
 |  | ||||||
|     void on_damage(int delta, int health_left); |  | ||||||
|     void on_death(int damage); |  | ||||||
| 
 |  | ||||||
|     virtual Health *get_health() override; |  | ||||||
|     virtual Health const *get_health() const override; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     float renav_time{0.f}; |  | ||||||
| 
 |  | ||||||
|     CharacterActor *target_player{nullptr}; |  | ||||||
|     Health *health{nullptr}; |  | ||||||
|     NavigationAgent3D *nav_agent{nullptr}; |  | ||||||
|     Area3D *vision_area{nullptr}; |  | ||||||
| 
 |  | ||||||
|     static float const RENAV_INTERVAL; |  | ||||||
| }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #endif // !ENEMY_HPP
 |  | ||||||
|  | @ -38,7 +38,7 @@ Vector3 GlobalWorldState::get_player_position() const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CharacterActor *GlobalWorldState::get_player_character() const { | CharacterActor *GlobalWorldState::get_player_character() const { | ||||||
|     return Ref<TunnelsGameMode>(GameRoot::get_singleton()->get_game_mode())->get_player_instance()->get_character(); |     return Ref<TunnelsGameMode>(GameRoot3D::get_singleton()->get_game_mode())->get_player_instance()->get_character(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Variant GlobalWorldState::get_world_property(StringName prop_key) { | Variant GlobalWorldState::get_world_property(StringName prop_key) { | ||||||
|  |  | ||||||
|  | @ -88,22 +88,28 @@ struct PlannerNodeHasher { | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static _FORCE_INLINE_ bool operator==(PlannerNode const &lhs, PlannerNode const &rhs) { | static _FORCE_INLINE_ | ||||||
|  | bool operator==(PlannerNode const &lhs, PlannerNode const &rhs) { | ||||||
|     return PlannerNodeHasher::hash(lhs) == PlannerNodeHasher::hash(rhs); |     return PlannerNodeHasher::hash(lhs) == PlannerNodeHasher::hash(rhs); | ||||||
| } | } | ||||||
| static _FORCE_INLINE_ bool operator!=(PlannerNode const &lhs, PlannerNode const &rhs) { | static _FORCE_INLINE_ | ||||||
|  | bool operator!=(PlannerNode const &lhs, PlannerNode const &rhs) { | ||||||
|     return !(lhs == rhs); |     return !(lhs == rhs); | ||||||
| } | } | ||||||
| static _FORCE_INLINE_ bool operator<(PlannerNode const &lhs, PlannerNode const &rhs) { | static _FORCE_INLINE_ | ||||||
|  | bool operator<(PlannerNode const &lhs, PlannerNode const &rhs) { | ||||||
|     return lhs.open_requirements.size() < rhs.open_requirements.size(); |     return lhs.open_requirements.size() < rhs.open_requirements.size(); | ||||||
| } | } | ||||||
| static _FORCE_INLINE_ bool operator>=(PlannerNode const &lhs, PlannerNode const &rhs) { | static _FORCE_INLINE_ | ||||||
|  | bool operator>=(PlannerNode const &lhs, PlannerNode const &rhs) { | ||||||
|     return !(lhs < rhs); |     return !(lhs < rhs); | ||||||
| } | } | ||||||
| static _FORCE_INLINE_ bool operator>(PlannerNode const &lhs, PlannerNode const &rhs) { | static _FORCE_INLINE_ | ||||||
|  | bool operator>(PlannerNode const &lhs, PlannerNode const &rhs) { | ||||||
|     return lhs.open_requirements.size() > rhs.open_requirements.size(); |     return lhs.open_requirements.size() > rhs.open_requirements.size(); | ||||||
| } | } | ||||||
| static _FORCE_INLINE_ bool operator<=(PlannerNode const &lhs, PlannerNode const &rhs) { | static _FORCE_INLINE_ | ||||||
|  | bool operator<=(PlannerNode const &lhs, PlannerNode const &rhs) { | ||||||
|     return !(lhs > rhs); |     return !(lhs > rhs); | ||||||
| } | } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| #include "register_types.h" | #include "register_types.h" | ||||||
| #include "action.hpp" | #include "action.hpp" | ||||||
| #include "character_actor.hpp" | #include "character_actor.hpp" | ||||||
|  | #include "character_awareness.hpp" | ||||||
| #include "character_data.hpp" | #include "character_data.hpp" | ||||||
| #include "enemy.hpp" |  | ||||||
| #include "global_world_state.hpp" | #include "global_world_state.hpp" | ||||||
| #include "goal_marker.hpp" | #include "goal_marker.hpp" | ||||||
| #include "health.hpp" | #include "health.hpp" | ||||||
|  | @ -33,7 +33,6 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level) | ||||||
|     if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { |     if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     ClassDB::register_abstract_class<GameRoot>(); |  | ||||||
|     ClassDB::register_class<GameMode>(); |     ClassDB::register_class<GameMode>(); | ||||||
|     ClassDB::register_class<GameRoot3D>(); |     ClassDB::register_class<GameRoot3D>(); | ||||||
|     ClassDB::register_class<GameState>(); |     ClassDB::register_class<GameState>(); | ||||||
|  | @ -45,7 +44,6 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level) | ||||||
|     ClassDB::register_class<TunnelsGameState>(); |     ClassDB::register_class<TunnelsGameState>(); | ||||||
|     ClassDB::register_class<TunnelsPlayer>(); |     ClassDB::register_class<TunnelsPlayer>(); | ||||||
| 
 | 
 | ||||||
|     ClassDB::register_class<Enemy>(); |  | ||||||
|     ClassDB::register_class<Health>(); |     ClassDB::register_class<Health>(); | ||||||
|     ClassDB::register_class<CharacterActor>(); |     ClassDB::register_class<CharacterActor>(); | ||||||
|     ClassDB::register_class<ProjectilePool>(); |     ClassDB::register_class<ProjectilePool>(); | ||||||
|  | @ -67,6 +65,7 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level) | ||||||
|     ClassDB::register_class<goap::Planner>(); |     ClassDB::register_class<goap::Planner>(); | ||||||
|     ClassDB::register_class<GoalMarker>(); |     ClassDB::register_class<GoalMarker>(); | ||||||
|     ClassDB::register_class<LineGoalMarker>(); |     ClassDB::register_class<LineGoalMarker>(); | ||||||
|  |     ClassDB::register_class<CharacterAwareness>(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" | extern "C" | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ void TunnelsGameMode::_bind_methods() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TunnelsGameMode::_begin() { | void TunnelsGameMode::_begin() { | ||||||
|     GameRoot::get_singleton()->connect("player_spawned", Callable(this, "on_player_spawned")); |     GameRoot3D::get_singleton()->connect("player_spawned", Callable(this, "on_player_spawned")); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TunnelsGameMode::on_player_spawned(Node *player) { | void TunnelsGameMode::on_player_spawned(Node *player) { | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ void TunnelsPlayer::_ready() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TunnelsPlayer::_exit_tree() { GDGAMEONLY(); | void TunnelsPlayer::_exit_tree() { GDGAMEONLY(); | ||||||
|     GameRoot::get_singleton()->remove_player(this->get_player_id()); |     GameRoot3D::get_singleton()->remove_player(this->get_player_id()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TunnelsPlayer::_process(double delta_time) { GDGAMEONLY(); | void TunnelsPlayer::_process(double delta_time) { GDGAMEONLY(); | ||||||
|  | @ -54,18 +54,13 @@ void TunnelsPlayer::_process(double delta_time) { GDGAMEONLY(); | ||||||
|     case State::ManualControl: |     case State::ManualControl: | ||||||
|         // send the current wasd input to the character
 |         // send the current wasd input to the character
 | ||||||
|         this->character->move(this->get_world_move_input().normalized()); |         this->character->move(this->get_world_move_input().normalized()); | ||||||
|  |         // fall through to shared functionality
 | ||||||
|  |     case State::Tactics: | ||||||
|         // send the current world cursor position the character
 |         // send the current world cursor position the character
 | ||||||
|         this->character->aim(mouse_world_location); |         this->character->aim(mouse_world_location); | ||||||
|         // move the camera along with the character
 |         // move the camera along with the character
 | ||||||
|         this->set_global_position(this->character->get_global_position()); |         this->set_global_position(this->character->get_global_position()); | ||||||
|         break; |         break; | ||||||
|     case State::Tactics: |  | ||||||
|         // move camera along with the input
 |  | ||||||
|         this->set_global_position(this->get_global_position() + this->get_world_move_input().normalized() * |  | ||||||
|                                   delta_time * TunnelsPlayer::TACTICS_MOVEMENT_SPEED); |  | ||||||
|         break; |  | ||||||
|     case State::Overview: |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -142,8 +137,6 @@ void TunnelsPlayer::fire_pressed(Ref<InputEvent> event, float value) { | ||||||
|         if(value == 1.f) |         if(value == 1.f) | ||||||
|             this->try_select_marker(); |             this->try_select_marker(); | ||||||
|         break; |         break; | ||||||
|     case State::Overview: |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -166,8 +159,9 @@ void TunnelsPlayer::try_select_marker() { | ||||||
|     if(marker == nullptr) |     if(marker == nullptr) | ||||||
|         return; |         return; | ||||||
|     UtilityFunctions::print("Hit: ", marker->get_path()); |     UtilityFunctions::print("Hit: ", marker->get_path()); | ||||||
|  |     // select a character to send orders to
 | ||||||
|     CharacterActor *target_character{nullptr}; |     CharacterActor *target_character{nullptr}; | ||||||
|     for(CharacterActor *loop_character : Ref<TunnelsGameMode>(GameRoot::get_singleton()->get_game_mode())->get_player_characters()) { |     for(CharacterActor *loop_character : Ref<TunnelsGameMode>(GameRoot3D::get_singleton()->get_game_mode())->get_player_characters()) { | ||||||
|         if(loop_character != this->character) { |         if(loop_character != this->character) { | ||||||
|             target_character = loop_character; |             target_character = loop_character; | ||||||
|             break; |             break; | ||||||
|  | @ -206,12 +200,12 @@ void TunnelsPlayer::initialize_character() { | ||||||
|     this->character = Object::cast_to<CharacterActor>(player_scene->instantiate()); |     this->character = Object::cast_to<CharacterActor>(player_scene->instantiate()); | ||||||
|     this->get_parent()->add_child(this->character); |     this->get_parent()->add_child(this->character); | ||||||
|     this->character->set_global_transform(this->get_global_transform()); |     this->character->set_global_transform(this->get_global_transform()); | ||||||
|     Ref<TunnelsGameState> game_state = GameRoot::get_singleton()->get_game_state(); |     Ref<TunnelsGameState> game_state = GameRoot3D::get_singleton()->get_game_state(); | ||||||
|     Ref<CharacterData> character = game_state->get_characters()[0]; |     Ref<CharacterData> character = game_state->get_characters()[0]; | ||||||
|     this->character->set_character_data(game_state->get_characters()[0]); |     this->character->set_character_data(game_state->get_characters()[0]); | ||||||
|     // disable navmesh navigation and start using player input
 |     // disable navmesh navigation and start using player input
 | ||||||
|     this->character->set_manual_mode(true); |     this->character->set_manual_mode(true); | ||||||
|     Ref<TunnelsGameMode>(GameRoot::get_singleton()->get_game_mode())->set_manual_character(this->character); |     Ref<TunnelsGameMode>(GameRoot3D::get_singleton()->get_game_mode())->set_manual_character(this->character); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Vector3 TunnelsPlayer::get_world_move_input() const { | Vector3 TunnelsPlayer::get_world_move_input() const { | ||||||
|  | @ -256,5 +250,4 @@ float const TunnelsPlayer::ROTATION_SPEED{0.5f}; | ||||||
| float const TunnelsPlayer::ROTATION_Y_MIN_INFLUENCE{7.f}; | float const TunnelsPlayer::ROTATION_Y_MIN_INFLUENCE{7.f}; | ||||||
| float const TunnelsPlayer::ROTATION_MARGIN{0.4f}; | float const TunnelsPlayer::ROTATION_MARGIN{0.4f}; | ||||||
| float const TunnelsPlayer::ROTATION_MARGIN_TACTICS_MUL{0.6f}; | float const TunnelsPlayer::ROTATION_MARGIN_TACTICS_MUL{0.6f}; | ||||||
| float const TunnelsPlayer::TACTICS_MOVEMENT_SPEED{10.f}; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -19,7 +19,6 @@ class TunnelsPlayer : public Node3D, public IPlayer { | ||||||
|     enum State { |     enum State { | ||||||
|         ManualControl = 0x0, |         ManualControl = 0x0, | ||||||
|         Tactics = 0x1, |         Tactics = 0x1, | ||||||
|         Overview = 0x2, |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|  | @ -66,7 +65,6 @@ private: | ||||||
|     static float const ROTATION_Y_MIN_INFLUENCE; |     static float const ROTATION_Y_MIN_INFLUENCE; | ||||||
|     static float const ROTATION_MARGIN_TACTICS_MUL; |     static float const ROTATION_MARGIN_TACTICS_MUL; | ||||||
|     static float const ROTATION_MARGIN; |     static float const ROTATION_MARGIN; | ||||||
|     static float const TACTICS_MOVEMENT_SPEED; |  | ||||||
| }; | }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| Subproject commit d81ad91a885a74338c02edf1d52a2fa5aa8746b6 | Subproject commit 2344f3f2b653dbf6a6c57c2e18c43a1f2c813024 | ||||||
		Loading…
	
		Reference in a new issue