CSC8503_Advanced_Game_Technologies / CSC8503 / CSC8503Common / AIOpponentObject.cpp
AIOpponentObject.cpp
Raw
#include "AIOpponentObject.h"

AIOpponentObject::AIOpponentObject(string filePath) : GameObject() {
	grid = new NavigationGrid(filePath);
	stateMachine = new StateMachine();

	initStateMachine();
}
void AIOpponentObject::initStateMachine() {
	State* raceToFinish = new State([&](float dt)->void {
		this->StartMovingAIOpponentObject(dt);
		}
	);
	State* teleportBehindPlayer = new State([&](float dt)->void {
		this->TeleportAIOpponentObject(dt);
		}
	);

	stateMachine->AddState(raceToFinish);
	stateMachine->AddState(teleportBehindPlayer);

	stateMachine->AddTransition(new StateTransition(teleportBehindPlayer, raceToFinish, [&]()->bool {
		return  stateSwitch == false;
		}
	));

	stateMachine->AddTransition(new StateTransition(raceToFinish, teleportBehindPlayer, [&]()->bool {
		return stateSwitch == true;
		}
	));
}

void AIOpponentObject::StartMovingAIOpponentObject(float dt) {
	NavigationPath path;
	aiOpponent->nodes.clear();
	aiOpponent->targetPosition = Vector3(0, 0, -105);

	if (aiOpponent->grid->FindPath(aiOpponent->GetTransform().GetPosition(), aiOpponent->targetPosition, path)) {
		Vector3 pos;
		while (path.PopWaypoint(pos)) {
			aiOpponent->nodes.push_back(pos);
		}

	//	if (aiOpponent->grid->FindPath(aiOpponent->GetTransform().GetPosition(), aiOpponent->targetPosition, path))	{
			//aiOpponent->optimiseNodes();
			aiOpponent->drawPath();

		if (aiOpponent->nodes.size() >= 2) //Sanity check
		{
			Vector3 direction = aiOpponent->nodes[1] - aiOpponent->nodes[0];
			direction.Normalise();
			aiOpponent->GetPhysicsObject()->AddForce(direction * aiOpponent->speed * aiOpponent->currentDT);
		}
		}
	//}

	teleportationCounter += dt;
	if (teleportationCounter >= 10.0) {
		teleportationCounter = 0.0f;
		
		if (((this->GetTransform().GetPosition().x - enemyPreviousPos.x) >= 5) &&
			 ((this->GetTransform().GetPosition().z - enemyPreviousPos.z) >= 5)) {				
				stateSwitch = true;
			}
		enemyPreviousPos = this->GetTransform().GetPosition();
		}
}

void AIOpponentObject::TeleportAIOpponentObject(float dt) {
	teleportationCounter = 0.0f;
	this->transform.SetPosition(player->GetTransform().GetPosition() + Vector3(0,0,15));
	stateSwitch = false;

	std::cout << "Player should be teleported";
}

void AIOpponentObject::UpdatePosition(float dt)
{
	currentDT = dt;

	/// if ai opponent not moving - teleport behind player

	stateMachine->Update(dt);
} 


int AIOpponentObject::PickUpItem(GameObject* object, int bonusValue) {
	AddPointsCollected(bonusValue);
	return pickedUpItems++;
}

void AIOpponentObject::optimiseNodes()
{
	RayCollision collision[3];
	vector<Vector3> optimisedNodes;
	Vector3 currentPosition = this->GetTransform().GetPosition();
	currentPosition.y = 0;
	optimisedNodes.push_back(currentPosition);
	for (int i = 0; i < nodes.size() - 1;)	{
		for (int j = i + 1; j < nodes.size(); j++) {
			Vector3 direction = nodes[j] - nodes[i];
			direction.Normalise();

			//Approximate rays to prevent getting stuck in an object
			Ray ray1(nodes[i], direction);
			Ray ray2(nodes[i] + Vector3::Cross(direction, Vector3(0, 1, 0)), direction);
			Ray ray3(nodes[i] - Vector3::Cross(direction, Vector3(0, 1, 0)), direction);

			//Find if there are obstacles on the path
			if (raycast(ray3, collision[2], true) &&
				raycast(ray2, collision[1], true) &&
				raycast(ray1, collision[0], true))
			{
				float distance = (nodes[j] - nodes[i]).Length();
				if ((collision[2].rayDistance < distance ||
					collision[1].rayDistance < distance ||
					collision[0].rayDistance < distance) && i != j - 1) //Obstacle detected and sanity check
				{
					//Add previous node to the optimised path
					optimisedNodes.push_back(nodes[j - 1]);
					i = j - 1;
					break;
				}
			}
			//Reached the end of path without encountering obstacles
			if (j == nodes.size() - 1) {
				direction = targetPosition - nodes[i];
				direction.Normalise();
				ray1 = Ray::Ray(nodes[i], direction);
				if (raycast(ray1, collision[0], true)) {
					float distance = (targetPosition - nodes[i]).Length();
					if (collision[0].rayDistance >= distance) {  //No obstacles on the way directly to target		
						optimisedNodes.push_back(targetPosition);
						nodes = optimisedNodes;
						return;
					}
				}
				//There was an obstacle so we need to go around
				optimisedNodes.push_back(nodes[j]);
				optimisedNodes.push_back(targetPosition);
				nodes = optimisedNodes;
				return;
			}
		}
	}
}

bool AIOpponentObject::raycast(Ray& r, RayCollision& closestCollision, bool object) const {
	RayCollision collision;
	for (auto& i : obstacles) {
		if (!i->GetBoundingVolume()) {
			continue;
		}
		RayCollision thisCollision;
		if (CollisionDetection::RayIntersection(r, *i, thisCollision)) {
			if (!object) {
				closestCollision = collision;
				closestCollision.node = i;
				return true;
			}
			else {
				if (thisCollision.rayDistance < collision.rayDistance) {
					thisCollision.node = i;
					collision = thisCollision;
				}
			}
		}
	}
	if (collision.node) {
		closestCollision = collision;
		closestCollision.node = collision.node;
		return true;
	}
	return false;
}

void AIOpponentObject::drawPath() {
	for (int i = 0; i < nodes.size() - 1; i++) {
		Debug::DrawLine(nodes[i] + Vector3(0, 1, 0), nodes[i + 1] + Vector3(0, 1, 0), Vector4(0, 1, 0, 1));
	}
}