126 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			3.2 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"
 | 
						|
 | 
						|
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");
 | 
						|
}
 | 
						|
 | 
						|
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<EnemyBody>::Size living{ this->npcs.size() };
 | 
						|
	// remove any dead npcs from the list
 | 
						|
	// leaving their bodies as separate nodes part of the tree
 | 
						|
	for (Vector<EnemyBody>::Size i{ 0 }; i < this->npcs.size(); ++i) {
 | 
						|
		EnemyBody *npc{ this->npcs[i] };
 | 
						|
		if (npc->get_health()->get_health() <= 0) {
 | 
						|
			--living;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// remove unit from world once all npcs are dead
 | 
						|
	if (living == 0) {
 | 
						|
		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));
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 |