From 28183fceb8ded746a5aca5f0f3233ee853a9f2e0 Mon Sep 17 00:00:00 2001 From: Sara Date: Sat, 25 Oct 2025 13:18:04 +0200 Subject: [PATCH] feat: fully implemented death in NpcUnit --- modules/wave_survival/enemy_body.cpp | 4 ++++ modules/wave_survival/npc_unit.cpp | 20 ++++++++++++---- modules/wave_survival/npc_unit.h | 1 + project/objects/enemies/enemy_wretched.tscn | 26 ++++++++++++++++++--- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/modules/wave_survival/enemy_body.cpp b/modules/wave_survival/enemy_body.cpp index 8bf9cbc6..25c792b8 100644 --- a/modules/wave_survival/enemy_body.cpp +++ b/modules/wave_survival/enemy_body.cpp @@ -19,11 +19,15 @@ void EnemyBody::on_child_added(Node *node) { void EnemyBody::ready() { this->fsm = cast_to(get_node(NodePath("%StateMachine"))); this->nav = cast_to(get_node(NodePath("%NavigationAgent3D"))); + this->health = cast_to(get_node(NodePath("%HealthStatus"))); } void EnemyBody::physics_process(double delta) { GETSET(velocity, { velocity = Vector3{ this->movement_direction.x * this->movement_speed, velocity.y, this->movement_direction.y * this->movement_speed } + (velocity * delta); + if (!is_on_floor() && this->health->get_health() > 0) { + velocity += get_gravity() * delta; + } }); if (!this->movement_direction.is_zero_approx()) { look_at(get_global_position() + Vector3{ this->movement_direction.x, 0.f, this->movement_direction.y }); diff --git a/modules/wave_survival/npc_unit.cpp b/modules/wave_survival/npc_unit.cpp index 65156b24..3f9f47aa 100644 --- a/modules/wave_survival/npc_unit.cpp +++ b/modules/wave_survival/npc_unit.cpp @@ -11,18 +11,28 @@ void NpcUnit::_bind_methods() { BIND_HPROPERTY(Variant::OBJECT, region, PROPERTY_HINT_NODE_TYPE, "MapRegion"); } +void NpcUnit::remove_npc(EnemyBody *npc) { + Transform3D const tf{ npc->get_global_transform() }; + remove_child(npc); + this->get_parent()->add_child(npc); + npc->set_global_transform(tf); +} + void NpcUnit::on_npc_death() { + Vector::Size living{ this->npcs.size() }; // remove any dead npcs from the list // leaving their bodies as separate nodes part of the tree - for (EnemyBody *npc : this->npcs) { + for (Vector::Size i{ 0 }; i < this->npcs.size(); ++i) { + EnemyBody *npc{ this->npcs[i] }; if (npc->get_health()->get_health() <= 0) { - this->get_parent()->add_child(npc); - this->npcs.erase(npc); - break; + --living; } } // remove unit from world once all npcs are dead - if (this->npcs.size() <= 0) { + if (living == 0) { + for (EnemyBody *npc : this->npcs) { + remove_npc(npc); + } this->set_region(nullptr); // de-register from region this->queue_free(); } diff --git a/modules/wave_survival/npc_unit.h b/modules/wave_survival/npc_unit.h index 142bd0f9..6140b233 100644 --- a/modules/wave_survival/npc_unit.h +++ b/modules/wave_survival/npc_unit.h @@ -10,6 +10,7 @@ class MapRegion; class NpcUnit : public Node3D { GDCLASS(NpcUnit, Node3D); static void _bind_methods(); + void remove_npc(EnemyBody *unit); void on_npc_death(); void on_npc_awareness_changed(bool seen); void child_added(Node *node); diff --git a/project/objects/enemies/enemy_wretched.tscn b/project/objects/enemies/enemy_wretched.tscn index f4b11c9b..e19edb72 100644 --- a/project/objects/enemies/enemy_wretched.tscn +++ b/project/objects/enemies/enemy_wretched.tscn @@ -7,23 +7,43 @@ script/source = "extends EnemyWretched @export var difficulty_weight : float = 10 -func _on_health_status_death() -> void: +var is_dead := false + +func on_death() -> void: %StateMachine.process_mode = Node.PROCESS_MODE_DISABLED %NavigationAgent3D.process_mode = Node.PROCESS_MODE_DISABLED - $wretched/AnimationPlayer.play(\"death\") $CollisionShape3D.disabled = true set_movement_direction(Vector2()) + +func _on_health_status_death() -> void: + $wretched/AnimationPlayer.play(\"death\") get_unit().region.raise_difficulty(1.0 / difficulty_weight) + on_death.call_deferred() + is_dead = true + +func _enter_tree() -> void: + if is_dead and $wretched/AnimationPlayer.current_animation != \"death\": + $wretched/AnimationPlayer.play.call_deferred(\"death\") + $wretched/AnimationPlayer.advance.call_deferred(INF) + elif is_dead: + $wretched/AnimationPlayer.play.call_deferred(\"death\") " [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ng1ul"] -radius = 0.26953125 +radius = 0.58691406 [node name="EnemyWretched" type="EnemyWretched"] +wall_min_slide_angle = 0.0 script = SubResource("GDScript_qot2n") [node name="wretched" parent="." instance=ExtResource("1_qot2n")] +[node name="Body" parent="wretched/Character/Skeleton3D" index="0"] +gi_mode = 0 + +[node name="club" parent="wretched/Character/Skeleton3D" index="1"] +gi_mode = 0 + [node name="Hitbox" parent="wretched/Character/Skeleton3D" index="2" node_paths=PackedStringArray("health")] health = NodePath("../../../../HealthStatus")