wave-survival/modules/wave_survival/npc_unit.cpp

140 lines
3.7 KiB
C++

#include "npc_unit.h"
#include "enemy_body.h"
#include "macros.h"
#include "map_region.h"
#include "patrol_path.h"
#include "player_detector.h"
#include "scene/resources/packed_scene.h"
void NpcUnit::_bind_methods() {
BIND_HPROPERTY(Variant::OBJECT, patrol_path, PROPERTY_HINT_NODE_TYPE, "PatrolPath");
BIND_PROPERTY(Variant::FLOAT, patrol_speed);
BIND_HPROPERTY(Variant::OBJECT, region, PROPERTY_HINT_NODE_TYPE, "MapRegion");
BIND_HPROPERTY(Variant::OBJECT, drop_on_clear, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
}
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(EnemyBody *npc) {
Vector<EnemyBody>::Size living{ this->npcs.size() };
for (Vector<EnemyBody>::Size i{ 0 }; i < this->npcs.size(); ++i) {
EnemyBody *npc{ this->npcs[i] };
if (npc->get_health()->get_health() <= 0) {
--living;
}
}
// all member NPCs are dead, remove self (leaving NPC bodies) and drop reward for player
if (living == 0) {
if (this->drop_on_clear.is_valid()) {
Node *node{ this->drop_on_clear->instantiate() };
if (Node3D * node3d{ cast_to<Node3D>(node) }) {
node3d->set_position(npc->get_global_position());
this->get_parent()->add_child(node3d);
}
}
for (EnemyBody *npc : this->npcs) {
remove_npc(npc);
}
this->set_region(nullptr); // de-register from region
this->queue_free();
}
}
void NpcUnit::on_npc_awareness_changed(bool value) {
if (value) {
this->aware_of_player = true;
}
}
void NpcUnit::child_added(Node *node) {
if (EnemyBody * npc{ cast_to<EnemyBody>(node) }) {
this->npcs.push_back(npc);
npc->set_unit(this);
Vector3 const offset{ npc->get_global_position() - get_global_position() };
npc->set_unit_offset({ offset.x, offset.z });
}
}
void NpcUnit::enter_tree() {
this->connect("child_entered_tree", callable_mp(this, &self_type::child_added));
}
void NpcUnit::ready() {
for (EnemyBody *npc : this->npcs) {
if (PlayerDetector * detector{ cast_to<PlayerDetector>(npc->get_node(NodePath("%PlayerDetector"))) }) {
detector->connect(PlayerDetector::sig_awareness_changed, callable_mp(this, &self_type::on_npc_awareness_changed));
}
if (HealthStatus * health{ npc->get_health() }) {
health->connect(HealthStatus::sig_death, callable_mp(this, &self_type::on_npc_death).bind(npc));
}
}
}
void NpcUnit::_notification(int what) {
// only run in-game
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
switch (what) {
default:
return;
case NOTIFICATION_ENTER_TREE:
enter_tree();
return;
case NOTIFICATION_READY:
ready();
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 this->aware_of_player;
}
void NpcUnit::set_patrol_speed(float speed) {
this->patrol_speed = speed;
}
float NpcUnit::get_patrol_speed() const {
return this->patrol_speed;
}
void NpcUnit::set_region(MapRegion *region) {
if (this->region == region) {
return; // don't re-register
}
if (this->region != nullptr) {
this->region->remove_unit(this);
this->region->disconnect(MapRegion::sig_phase_changed, callable_mp(this, &self_type::on_npc_awareness_changed));
}
this->region = region;
if (region != nullptr) {
region->register_unit(this);
region->connect(MapRegion::sig_phase_changed, callable_mp(this, &self_type::on_npc_awareness_changed));
}
}
MapRegion *NpcUnit::get_region() const {
return this->region;
}
void NpcUnit::set_drop_on_clear(Ref<PackedScene> object) {
this->drop_on_clear = object;
}
Ref<PackedScene> NpcUnit::get_drop_on_clear() const {
return this->drop_on_clear;
}