/*
*	AI.cpp
*	----------------------------------------
*	A part of "Intelligent AI"
*	© 2001 SCC Team 064
*	----------------------------------------
*
*	This file:
*		€ Contains a series of evaluators that determine the effectiveness 
*		of certian actions.
*/


#include <math.h>

#include "Project.h"


/*------------------------------------------------------------------------------------------*/
/*								ARTIFICIAL INTELLIGENCE FUNCTIONS							*/
/*------------------------------------------------------------------------------------------*/



/*										EvolvingAI									*/
/*----------------------------------------------------------------------------------*/
/*
* Contains AI with various, changing, improving implementations that allow it to
* out-perform the Constant AI.
*/
void EvolvingAI(int i)
{
	int x, totalFavorable;
	int favorable[4];				// Array of favorable conditions. Conditions can be assigned
									// greater or lesser value. Used to assess when to attack or evade.
	double targetX, targetZ,		// Target azimuths in the horizontal (x) and vertical (z) planes.
		xDist, yDist, zDist;		// Distances between the planes.
	int pSeperation;				// Total seperation between the planes.

	totalFavorable = 0;				// Initializes overall favorableness

	// Find which player is the enemy.
	for (int enemy = 0; enemy < getCurrentNumPlayers(); enemy++)
		if (enemy != i)
			x = enemy;

	// Find total distance between the players.
	yDist = player[i].yPos - player[x].yPos;
	xDist = player[i].xPos - player[x].xPos;
	zDist = player[i].zPos - player[x].zPos;

	pSeperation = sqrt( (xDist * xDist) + (yDist * yDist) + (zDist * zDist) );

	// Calculate the angle between the players based on the slope of the line connecting them
	// in the horizontal and vertical planes.
	targetX = atan( (player[x].yPos - player[i].yPos)/(player[x].xPos - player[i].xPos) );

	targetZ = atan( (player[x].zPos - player[i].zPos)/
				sqrt((player[x].xPos - player[i].xPos) * (player[x].xPos - player[i].xPos) + 
				(player[x].yPos - player[i].yPos) * (player[x].yPos - player[i].yPos)) );

	// Account for the nature of slopes (i.e. one slope could designate two angles 180º apart).
	if (player[x].xPos - player[i].xPos < 0)
		targetX += M_PI;

	if (player[x].zPos - player[i].zPos < 0)
		targetZ += M_PI;


	/* Favorable Conditions in the Horizontal Plane */

	// Favorable Condition 1: True if the enemy is not facing the player.
	if	(
			((player[i].yPos <= player[x].yPos) && (player[i].xPos >= player[x].xPos) && 
				(player[x].yaw >= 0) && (player[x].yaw <= 3. * M_PI / 2.)) ||
			((player[i].yPos <= player[x].yPos) && (player[i].xPos <= player[x].xPos) && 
				( ((player[x].yaw >= 0) && (player[x].yaw <= M_PI)) || 
			((player[x].yaw >= 3. * M_PI / 2.) && (player[x].yaw <= 2. * M_PI)) ) ) ||
			((player[i].yPos >= player[x].yPos) && (player[i].xPos >= player[x].xPos) && 
				( ((player[x].yaw >= 0) && (player[x].yaw <= M_PI / 2.)) || 
			((player[x].yaw >= M_PI) && (player[x].yaw <= 2. * M_PI)) ) ) ||
			((player[i].yPos >= player[x].yPos) && (player[i].xPos <= player[x].xPos) && 
				(player[x].yaw >= M_PI / 2.) && (player[x].yaw <= 2. * M_PI))
		)
			favorable[0] = 1;
	else
	{
			favorable[0] = 0;
			targetZ = -targetZ;			// Alter pitch to fly towards the enemy whether 
	}									// behind or in front of it.


	// Favorable Condition 2: True if the player is facing the enemy.
	if	(!	(
			((player[x].yPos <= player[i].yPos) && (player[x].xPos >= player[i].xPos) && 
				(player[i].yaw >= 0) && (player[i].yaw <= 3. * M_PI / 2.)) ||
			((player[x].yPos <= player[i].yPos) && (player[x].xPos <= player[i].xPos) && 
				( ((player[i].yaw >= 0) && (player[i].yaw <= M_PI)) || 
			((player[i].yaw >= 3. * M_PI / 2.) && (player[i].yaw <= 2. * M_PI)) ) ) ||
			((player[x].yPos >= player[i].yPos) && (player[x].xPos >= player[i].xPos) && 
				( ((player[i].yaw >= 0) && (player[i].yaw <= M_PI / 2.)) || 
			((player[i].yaw >= M_PI) && (player[i].yaw <= 2. * M_PI)) ) ) ||
			((player[x].yPos >= player[i].yPos) && (player[x].xPos <= player[i].xPos) && 
				(player[i].yaw >= M_PI / 2.) && (player[i].yaw <= 2. * M_PI))
			)
		)
			favorable[1] = 1;
	else
			favorable[1] = 0;

	// Favorable Condition 3: True if the player is behind the enemy. This condition is given more weight.
	if ( ((player[x].yaw - targetX <= M_PI) && (player[x].yaw - targetX >= 0))
			 || ((player[x].yaw - targetX >= - M_PI) && (player[x].yaw - targetX <= 0)) )
		favorable[2] = 2;
	else
		favorable[2] = 0;

	// Favorable Condition 4: True if the players are a far distance away from eachother.
	// 						  This allows a player to be more aggressive in attacking if it is
	//						  a far distance away from its opponent.
	if ( pSeperation > 100 )
	{
		favorable[3] = 1;
	}
	else
		favorable[3] = 0;

	// Totals the overall favorableness of the current situation.
	for (int i = 0; i < 3; i++)
		totalFavorable += favorable[i];

	/* Determine Whether To Attack or Evade in the Horizontal Plane. */

	if	(totalFavorable >= 2)	// If a certain overall favorableness is obtained, the plane attacks.
	{
		// If the planes are not moderately close, the horiz. target azimuth is rounded to a predetermined
		// measurement in the center of the 45º section of the horizontal plane where the azimuth resides.
		// This allows the player to fly in the general direction of the opponent, as opposed to directly
		// at it, so that it can perhaps cut it off.
		if (pSeperation > 20)
		{
			if ((targetX >= 0 && targetX < M_PI / 8.) || (targetX >= 15. * M_PI / 8. && 
					targetX < targetX < 2. * M_PI))
				targetX = 0;
			if (targetX >= M_PI / 8. && targetX < 3. * M_PI / 8.)
				targetX = M_PI / 4.;
			if (targetX >= 3. * M_PI / 8. && targetX < 5. * M_PI / 8.)
				targetX = M_PI / 2.;
			if (targetX >= 5. * M_PI / 8. && targetX < 7. * M_PI / 8.)
				targetX = 3. * M_PI / 4.;
			if (targetX >= 7. * M_PI / 8. && targetX < 9. * M_PI / 8.)
				targetX = M_PI;
			if (targetX >= 9. * M_PI / 8. && targetX < 11. * M_PI / 8.)
				targetX = 5. * M_PI / 4.;
			if (targetX >= 11. * M_PI / 8. && targetX < 13. * M_PI / 8.)
				targetX = 3. * M_PI / 2.;
			if (targetX >= 13. * M_PI / 8. && targetX < 15. * M_PI / 8.)
				targetX = 7. * M_PI / 4.;
		}

		player[i].targetYaw 	= targetX;		// Set the player's targetYaw to the calculated target.
	}
	else	// If a high enough overall favorableness is not obtained, the plane evades.
	{
		// Evading is accomplished effectively by flying perpendicular to the enemy; this also can
		// help place the plane in a good position to attack.
		player[i].targetYaw 	= targetX + M_PI / 2.;
	}


	/* Favorable Conditions in the Vertical Plane */

	// Favorable Condition 1: True if the enemy is not facing the player.
	if	(
			((player[i].zPos <= player[x].zPos) && (player[i].xPos >= player[x].xPos) && 
				(player[x].pitch >= 0) && (player[x].pitch <= 3. * M_PI / 2.)) ||
			((player[i].zPos <= player[x].zPos) && (player[i].xPos <= player[x].xPos) && 
				( ((player[x].pitch >= 0) && (player[x].pitch <= M_PI)) || 
			((player[x].pitch >= 3. * M_PI / 2.) && (player[x].pitch <= 2. * M_PI)) ) ) ||
			((player[i].zPos >= player[x].zPos) && (player[i].xPos >= player[x].xPos) && 
				( ((player[x].pitch >= 0) && (player[x].pitch <= M_PI / 2.)) || 
			((player[x].pitch >= M_PI) && (player[x].pitch <= 2. * M_PI)) ) ) ||
			((player[i].zPos >= player[x].zPos) && (player[i].xPos <= player[x].xPos) && 
				(player[x].pitch >= M_PI / 2.) && (player[x].pitch <= 2. * M_PI))
		)
			favorable[0] = 1;
	else
			favorable[0] = 0;

	// Favorable Condition 2: True if the player is facing the enemy.
	if	(!	(
			((player[x].zPos <= player[i].zPos) && (player[x].xPos >= player[i].xPos) && 
				(player[i].pitch >= 0) && (player[i].pitch <= 3. * M_PI / 2.)) ||
			((player[x].zPos <= player[i].zPos) && (player[x].xPos <= player[i].xPos) && 
				( ((player[i].pitch >= 0) && (player[i].pitch <= M_PI)) || 
			((player[i].pitch >= 3. * M_PI / 2.) && (player[i].pitch <= 2. * M_PI)) ) ) ||
			((player[x].zPos >= player[i].zPos) && (player[x].xPos >= player[i].xPos) && 
				( ((player[i].pitch >= 0) && (player[i].pitch <= M_PI / 2.)) || 
			((player[i].pitch >= M_PI) && (player[i].pitch <= 2. * M_PI)) ) ) ||
			((player[x].zPos >= player[i].zPos) && (player[x].xPos <= player[i].xPos) && 
				(player[i].pitch >= M_PI / 2.) && (player[i].pitch <= 2. * M_PI))
			)
		)
			favorable[1] = 1;
	else
			favorable[1] = 0;

	// Favorable Condition 3: True if the player is behind the enemy. This condition is given more weight.
	if ( ((player[x].pitch - targetZ <= M_PI) && (player[x].pitch - targetZ >= 0))
			 || ((player[x].pitch - targetZ >= - M_PI) && (player[x].pitch - targetZ <= 0)) )
		favorable[2] = 2;
	else
		favorable[2] = 0;

	// Favorable Condition 4: True if the players are a far distance away from eachother.
	// 						  This allows a player to be more aggressive in attacking if it is
	//						  a far distance away from its opponent.
	for (int i = 0; i < 3; i++)
		totalFavorable += favorable[i];

	/* Determine Whether To Attack or Evade in the Vertical Plane. */

	if	(totalFavorable >= 2)	// If a certain overall favorableness is obtained, the plane attacks.
	{
		// If the planes are not moderately close, the vert. target azimuth is rounded to a predetermined
		// measurement in the center of the 45º section of the vertical plane where the azimuth resides.
		if (pSeperation > 20)
		{
			if ((targetZ >= 0 && targetZ < M_PI / 8.) || (targetZ >= 15. * M_PI / 8. && 
					targetZ < targetZ < 2. * M_PI))
				targetZ = 0;
			if (targetZ >= M_PI / 8. && targetZ < 3. * M_PI / 8.)
				targetZ = M_PI / 4.;
			if (targetZ >= 3. * M_PI / 8. && targetZ < 5. * M_PI / 8.)
				targetZ = M_PI / 2.;
			if (targetZ >= 5. * M_PI / 8. && targetZ < 7. * M_PI / 8.)
				targetZ = 3. * M_PI / 4.;
			if (targetZ >= 7. * M_PI / 8. && targetZ < 9. * M_PI / 8.)
				targetZ = M_PI;
			if (targetZ >= 9. * M_PI / 8. && targetZ < 11. * M_PI / 8.)
				targetZ = 5. * M_PI / 4.;
			if (targetZ >= 11. * M_PI / 8. && targetZ < 13. * M_PI / 8.)
				targetZ = 3. * M_PI / 2.;
			if (targetZ >= 13. * M_PI / 8. && targetZ < 15. * M_PI / 8.)
				targetZ = 7. * M_PI / 4.;
		}
		player[i].targetPitch	= targetZ;		// Set the player's targetPitch to the calculated target.
	}
	else	// If a high enough overall favorableness is not obtained, the plane evades.
	{
		// Evading is accomplished effectively by flying perpendicular to the enemy; this also can
		// help place the plane in a good position to attack.
		player[i].targetPitch	= targetZ + M_PI / 2.;
	}

	player[i].targetRoll 	= player[x].roll;

	/* Continuously Called Functions */

	player[i].UnitRange();					// Determines whether or not the enemy is in range to attack.
	player[i].Move();						// Performs initial calculations for moving.
	player[i].ContinueMoving();				// Continues more advanced moving calculations.
	player[i].CollisionDetection(player[x]);// Checks to determine if the two players are in contact.
	AutoAttack(i);							// Attacks the other player if within range.
}


/*										ConstantAI									*/
/*----------------------------------------------------------------------------------*/
/*
* Primitive AI with which the effectiveness of the Evolving AI is compared. Remains
* constant.
*/
void ConstantAI(int i)
{
	int x;
	double targetX, targetZ;

	for (int enemy = 0; enemy < getCurrentNumPlayers(); enemy++)
		if (enemy != i)
			x = enemy;

	targetX = atan( (player[x].yPos - player[i].yPos)/(player[x].xPos - player[i].xPos) );

	targetZ = atan( (player[x].zPos - player[i].zPos)/
				sqrt((player[x].xPos - player[i].xPos) * (player[x].xPos - player[i].xPos) + 
				(player[x].yPos - player[i].yPos) * (player[x].yPos - player[i].yPos)) );

	if (player[x].xPos - player[i].xPos < 0)
		targetX += M_PI;

	if	(
			((player[i].yPos <= player[x].yPos) && (player[x].yaw <= M_PI)) ||
			((player[i].yPos >= player[x].yPos) && (player[x].yaw >= M_PI))
		)
	{
			player[i].targetYaw 	= targetX;
	}
	else
	{
			player[i].targetYaw 	= targetX + M_PI / 2.;
	}

	player[i].targetPitch 	= targetZ;
	player[i].targetRoll 	= player[x].roll;

	player[i].UnitRange();
	player[i].Move();
	player[i].ContinueMoving();
	player[i].CollisionDetection(player[x]);
	AutoAttack(i);
}


/*										HumanI										*/
/*----------------------------------------------------------------------------------*/
/*
* Real intelligence.  This allows a human to control a player's plane via the keyboard,
* allowing for effective testing of both the Constant and Evolving AIs, and also the
* capabilities of the program (ensuring movement, physics, etc. work correctly).
*/
void HumanAI(int i)
{
	int x;

	for (int enemy = 0; enemy < getCurrentNumPlayers(); enemy++)
		if (enemy != i)
			x = enemy;

	if (Right)	// Alters the plane's yaw by a small factor so as to turn right.
		player[i].targetYaw -= M_PI / 500.;
	if (Left)
		player[i].targetYaw += M_PI / 500.;

	if (Up)
		player[i].targetPitch += M_PI / 1000.;
	if (Down)
		player[i].targetPitch -= M_PI / 1000.;

	player[i].UnitRange();
	player[i].Move();
	player[i].ContinueMoving();
	player[i].CollisionDetection(player[x]);
	AutoAttack(i);
}


/*											AutoAttack											*/
/*----------------------------------------------------------------------------------------------*/
/* 
* Controls attack rate, calling the attack function when the unit is both in the presence of an
* enemy and elegible to attack (based on attack rate).  That way, when the opponent is attacked, the
* auto attack function can also be called by the opponent in retaliation
*/
void AutoAttack(int j)
{
	for (int i = 0; i < getCurrentNumPlayers(); i++)
	{
		if (player[j].canSee[i])
		{
			player[j].Attack(player[i]);
		}
	}
}