//============================================================
//============================================================
//==														==
//==		Computer Persona Handling Routines				==
//==														==
//============================================================
//============================================================


//========================================================  Includes  

#include "Globals.h"
#include "UnivUtilities.h"
#include "Computer.h"
#include "Dynamics.h"
#include "SoundUtils.h"


//========================================================  Functions  
//========================================================  DiagonalToPlayer  

short DiagonalToPerson (playerType *who)

{
	short		newDirection;
	
	if (who->selector == kOpponentSelector)
	{
		if (thePlayer.xPos > theOpponent.xPos)
		{
			if (thePlayer.zPos > theOpponent.zPos)
				newDirection = kFacingNorthEast;
			else
				newDirection = kFacingSouthEast;
		}
		else
		{
			if (thePlayer.zPos > theOpponent.zPos)
				newDirection = kFacingNorthWest;
			else
				newDirection = kFacingSouthWest;
		}
	}
	else
	{
		if (theOpponent.xPos > thePlayer.xPos)
		{
			if (theOpponent.zPos > thePlayer.zPos)
				newDirection = kFacingNorthEast;
			else
				newDirection = kFacingSouthEast;
		}
		else
		{
			if (theOpponent.zPos > thePlayer.zPos)
				newDirection = kFacingNorthWest;
			else
				newDirection = kFacingSouthWest;
		}
	}
	
	return (newDirection);
}


//========================================================  DiagonalToBall  

short DiagonalToBall (playerType *who)

{
	short		newDirection;
	
	if (theBall.xPos > who->xPos)
	{
		if (theBall.zPos > who->zPos)
			newDirection = kFacingNorthEast;
		else
			newDirection = kFacingSouthEast;
	}
	else
	{
		if (theBall.zPos > who->zPos)
			newDirection = kFacingNorthWest;
		else
			newDirection = kFacingSouthWest;
	}
	return (newDirection);
}


//========================================================  OctalToPoint  

short OctalToPoint (playerType *who, register short xPos, register short zPos)

{
	register long	rise, run;
	short			newDirection;
	
	if (xPos > who->xPos)
	{
		if (zPos > who->zPos)
			newDirection = kFacingNorthEast;
		else
			newDirection = kFacingSouthEast;
	}
	else
	{
		if (zPos > who->zPos)
			newDirection = kFacingNorthWest;
		else
			newDirection = kFacingSouthWest;
	}
	
	switch (newDirection)
	{
		case kFacingNorthEast:
			rise = (long)zPos - (long)who->zPos;
			run = (long)xPos - (long)who->xPos;
			if ((rise * 2) < run)
			{
				newDirection = kFacingEast;
			}
			else if (rise > (run * 2))
			{
				newDirection = kFacingNorth;
			}
			else
			{
				newDirection = kFacingNorthEast;
			}
			break;
		case kFacingNorthWest:
			rise = (long)zPos - (long)who->zPos;
			run = (long)who->xPos - (long)xPos;
			if ((rise * 2) < run)
			{
				newDirection = kFacingWest;
			}
			else if (rise > (run * 2))
			{
				newDirection = kFacingNorth;
			}
			else
			{
				newDirection = kFacingNorthWest;
			}
			break;
		case kFacingSouthEast:
			rise = (long)who->zPos - (long)zPos;
			run = (long)xPos - (long)who->xPos;
			if ((rise * 2) < run)
			{
				newDirection = kFacingEast;
			}
			else if (rise > (run * 2))
			{
				newDirection = kFacingSouth;
			}
			else
			{
				newDirection = kFacingSouthEast;
			}
			break;
		case kFacingSouthWest:
			rise = (long)who->zPos - (long)zPos;
			run = (long)who->xPos - (long)xPos;
			if ((rise * 2) < run)
			{
				newDirection = kFacingWest;
			}
			else if (rise > (run * 2))
			{
				newDirection = kFacingSouth;
			}
			else
			{
				newDirection = kFacingSouthWest;
			}
			break;
	}
	return (newDirection);
}


//========================================================  LookUpBoardForce  

void LookUpBoardForce (playerType *who)

{
	short			boardXForce, boardZForce;
	
	if ((who->justHitWall == 0) && (who->justHitOpponent == 0))
	{
		boardXForce = boardForceTable[who->direction][kXComponent];
		boardZForce = boardForceTable[who->direction][kZComponent];
		
		if (who->bashApplied)
		{
			boardXForce *= 4;								// 200% inc in board force 
			boardZForce *= 4;
		}
		
		if (who->posture == kCrouching)
		{
			boardXForce += boardXForce / kAddForceFract;	// 25% inc in board force 
			boardZForce += boardZForce / kAddForceFract;	// tried 50%, but harsh 
		}
	
		who->xVel += boardXForce;
		who->zVel += boardZForce;
	}
}


//========================================================  NearTheBall  

Boolean NearTheBall (playerType *who)

{
	#define			kNearDistSquared 16777216L	// a distance of 64 
	register long	xDistance, zDistance, distanceSquared;
	Boolean			nearBall;
	
	xDistance = who->xPos - theBall.xPos;
	zDistance = who->zPos - theBall.zPos;
	distanceSquared = (xDistance * xDistance) + (zDistance * zDistance);
	
	if (distanceSquared < kNearDistSquared)
		nearBall = TRUE;
	else
		nearBall = FALSE;
	
	return (nearBall);
}


//========================================================  NearTheEdge  

Boolean NearTheEdge (playerType *who)

{
	#define			kSmEdgeDistSquared 108826624L	// a distance of 163 
	#define			kLgEdgeDistSquared 170459136L	// a distance of 204 
	register long	distanceSquared;
	Boolean			nearEdge;
	
	distanceSquared = ((long)who->xPos * (long)who->xPos) + 
			((long)who->zPos * (long)who->zPos);
	if (arenaSize == kLargeArena)
	{
		if (distanceSquared > kLgEdgeDistSquared)
			nearEdge = TRUE;
		else
			nearEdge = FALSE;
	}
	else
	{
		if (distanceSquared > kLgEdgeDistSquared)
			nearEdge = TRUE;
		else
			nearEdge = FALSE;
	}
	
	return(nearEdge);
}


//========================================================  NearTheEdgeSmarter  

Boolean NearTheEdgeSmarter (playerType *who)

{
	#define			kFramesConsidered	8L
	#define			kSmEdgeDistSquared 108826624L	// a distance of 163 
	#define			kLgEdgeDistSquared 170459136L	// a distance of 204 
	register long	distanceSquared;
	long			anticipatedX, anticipatedZ;
	Boolean			nearEdge;
	
	anticipatedX = (long)who->xPos + ((long)(who->xVel / 
			kVelocitySensitive) * kFramesConsidered);
	anticipatedZ = (long)who->zPos + ((long)(who->zVel / 
			kVelocitySensitive) * kFramesConsidered);
	distanceSquared = (anticipatedX * anticipatedX) + (anticipatedZ * anticipatedZ);
	
	if (arenaSize == kLargeArena)
	{
		if (distanceSquared > kLgEdgeDistSquared)
			nearEdge = TRUE;
		else
			nearEdge = FALSE;
			
	}
	else
	{
		if (distanceSquared > kSmEdgeDistSquared)
			nearEdge = TRUE;
		else
			nearEdge = FALSE;
	}
	
	return(nearEdge);
}


//========================================================  NearTheGoal  

Boolean NearTheGoal (playerType *who)

{
	#define			kSmGoalXThreshRt	9123	// 84% of distance (10861) 
	#define			kSmGoalXThreshLf	-9123	// 84% of distance from center 
	#define			kSmGoalZThresh		9123	// 84% of distance from center 
	#define			kLgGoalXThreshRt	11811	// 87% of distance from center 
	#define			kLgGoalXThreshLf	-11811	// 87% of distance from center 
	#define			kLgGoalZThresh		11811	// 87% of distance from center 
	Boolean			nearGoal;
	
	if (arenaSize == kLargeArena)
	{
		if (who->whichGoal == kRightGoal)
		{
			if ((who->xPos > kLgGoalXThreshRt) && (who->zPos > kLgGoalZThresh))
				nearGoal = TRUE;
			else
				nearGoal = FALSE;
		}
		else
		{
			if ((who->xPos < kLgGoalXThreshLf) && (who->zPos > kLgGoalZThresh))
				nearGoal = TRUE;
			else
				nearGoal = FALSE;
		}
	}
	else
	{
		if (who->whichGoal == kRightGoal)
		{
			if ((who->xPos > kSmGoalXThreshRt) && (who->zPos > kSmGoalZThresh))
				nearGoal = TRUE;
			else
				nearGoal = FALSE;
		}
		else
		{
			if ((who->xPos < kSmGoalXThreshLf) && (who->zPos > kSmGoalZThresh))
				nearGoal = TRUE;
			else
				nearGoal = FALSE;
		}
	}
	
	return (nearGoal);
}


//========================================================  NearPerson  

Boolean NearPerson (void)

{
	#define			kNearPlayerDistSqr 58982400L	// a distance of 120 
	register long	xDistance, zDistance, distanceSquared;
	Boolean			nearPlayer;
	
	xDistance = theOpponent.xPos - thePlayer.xPos;
	zDistance = theOpponent.zPos - thePlayer.zPos;
	distanceSquared = (xDistance * xDistance) + (zDistance * zDistance);
	
	if (distanceSquared < kNearPlayerDistSqr)
		nearPlayer = TRUE;
	else
		nearPlayer = FALSE;
	
	return (nearPlayer);
}


//========================================================  DecentShot  

Boolean DecentShot (playerType *who)

{
	#define kFramesToTrackBall	8
	register long	newVelX, newVelZ;
	register long	newPosX, newPosZ;
	short			i, ballsPlace, indexX, indexZ;
	Boolean			shootIt;
	
	newVelX = (long)who->xVel / kVelocitySensitive;
	newVelZ = (long)who->zVel / kVelocitySensitive;
	
	newPosX = (long)who->xPos;
	newPosZ = (long)who->zPos;
	
	shootIt = FALSE;
	
	for (i = 0; i < kFramesToTrackBall; i++)
	{
		newPosX += newVelX;
		newPosZ += newVelZ;
		
		indexX  = abs(newPosX / 512);
		indexZ  = abs(newPosZ / 512);
		
		ballsPlace = *(littleForceTable + indexX * 82 + indexZ * 2 + kXComponent);
		
		if (((ballsPlace == kBackBoard) || (ballsPlace == kGoalPath)) && 
				(newPosZ > 0) && (((who->whichGoal == kRightGoal) && 
				(newPosX > 0)) || ((who->whichGoal == kLeftGoal) && (newPosX < 0))))
		{
			shootIt = TRUE;
			break;
		}
		else if (ballsPlace == kOutOBounds)
		{
			break;
		}
	}
	
	return (shootIt);
}


//========================================================  ExcellentShot  

Boolean ExcellentShot (playerType *who)

{
	#define kFramesToTrackBall	8
	register long	newVelX, newVelZ;
	register long	newPosX, newPosZ;
	short			i, ballsPlace, indexX, indexZ;
	Boolean			shootIt;
	
	newVelX = (long)who->xVel / kVelocitySensitive;
	newVelZ = (long)who->zVel / kVelocitySensitive;
	
	newPosX = (long)who->xPos;
	newPosZ = (long)who->zPos;
	
	shootIt = FALSE;
	
	for (i = 0; i < kFramesToTrackBall; i++)
	{
		newPosX += newVelX;
		newPosZ += newVelZ;
		
		indexX  = abs(newPosX / 512);
		indexZ  = abs(newPosZ / 512);
		
		ballsPlace = *(proForceTable + indexX * 82 + indexZ * 2 + kXComponent);
		
		if ((ballsPlace == kGoalPath) && (newPosZ > 0) && 
				(((who->whichGoal == kRightGoal) && (newPosX > 0)) || 
				((who->whichGoal == kLeftGoal) && (newPosX < 0))))
		{
			shootIt = TRUE;
			break;
		}
		else if (ballsPlace == kOutOBounds)
		{
			break;
		}
	}
	
	return (shootIt);
}


//========================================================  ExcellentShot  

Boolean ExcellentDirectionalShot (playerType *who, short directed)

{
	#define kFramesTrackDirBall	8
	register short	newPosX, newPosZ;
	short			newVelX, newVelZ;
	short			i, ballsPlace, indexX, indexZ;
	Boolean			shootIt;
	
	newVelX = boardForceTable[directed][kXComponent] * kPersonImpulse;
	newVelZ = boardForceTable[directed][kZComponent] * kPersonImpulse;
	
	newPosX = who->xPos + newVelX;
	newPosZ = who->zPos + newVelZ;
	
	newVelX += who->xVel / kVelocitySensitive;
	newVelZ += who->zVel / kVelocitySensitive;
	
	shootIt = FALSE;
	
	for (i = 0; i < kFramesTrackDirBall; i++)
	{
		newPosX += newVelX;
		newPosZ += newVelZ;
		
		indexX  = abs(newPosX / 512);
		indexZ  = abs(newPosZ / 512);
		
		ballsPlace = *(littleForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				
		if (who->whichGoal == kRightGoal)
		{
			if ((ballsPlace == kGoalPath) && (newPosZ > 0) && (newPosX > 0))
			{
				shootIt = TRUE;
				break;
			}
			else if ((ballsPlace == kOutOBounds) || (ballsPlace == kBackBoard))
			{
				break;
			}
		}
		else
		{
			if ((ballsPlace == kGoalPath) && (newPosZ > 0) && (newPosX < 0))
			{
				shootIt = TRUE;
				break;
			}
			else if ((ballsPlace == kOutOBounds) || (ballsPlace == kBackBoard))
			{
				break;
			}
		}
	}
	
	return (shootIt);
}


//========================================================  PointingToDisaster  

Boolean PointingToDisaster (playerType *who)

{
	#define			kFarDisasterLookAhead	128L	// was 144L 
	#define 		kSmDisasterSquared	235929600L	// a distance of 240 
	#define			kLgDisasterSquared	368640000L	// a distance of 300 
	long			possibleX, possibleZ, distanceSquared;
	Boolean			nearDisaster;
	
	possibleX = (long)who->xPos + (long)boardForceTable[who->direction][kXComponent] * 
			kFarDisasterLookAhead;
	possibleZ = (long)who->zPos + (long)boardForceTable[who->direction][kZComponent] * 
			kFarDisasterLookAhead;
	
	distanceSquared = (possibleX * possibleX) + (possibleZ * possibleZ);
	
	if (arenaSize == kLargeArena)
	{
		if (distanceSquared > kLgDisasterSquared)
		{
			nearDisaster = TRUE;
		}
		else
		{
			nearDisaster = FALSE;
		}
	}
	else
	{
		if (distanceSquared > kSmDisasterSquared)
		{
			nearDisaster = TRUE;
		}
		else
		{
			nearDisaster = FALSE;
		}
	}
	
	return(nearDisaster);
}


//========================================================  RunTheDiagonalNE  

short RunTheDiagonalNE (playerType *who)

{
	short			newDirection;
	
	switch (who->direction)
	{
		case kFacingNorthEast:
			if ((who->xVel < 0) && (who->zVel < 0))
			{
				if ((who->xPos < 0) && (who->zPos < 0))
					newDirection = kFacingNorthEast;
				else
					newDirection = kFacingSouthWest;
			}
			else
			{
				newDirection = kFacingNorthEast;
				if (NearTheEdge(who) && (who->xPos > 0) && (who->zPos > 0))
					DoPersonBallParted(who);			// shoot that ball! 
			}
			break;
		case kFacingSouthWest:
			if ((who->xVel > 0) && (who->zVel > 0))
			{
				if ((who->xPos > 0) && (who->zPos > 0))
					newDirection = kFacingSouthWest;
				else
					newDirection = kFacingNorthEast;
			}
			else
			{
				if (NearTheEdge(who) && (who->xPos < 0) && (who->zPos < 0))
					newDirection = kFacingNorthEast;
				else
					newDirection = kFacingSouthWest;
			}
			break;
		default:
			newDirection = kFacingSouthWest;
			break;
	}
	
	return (newDirection);
}


//========================================================  RunTheDiagonalNW  

short RunTheDiagonalNW (playerType *who)

{
	short			newDirection;
	
	switch (who->direction)
	{
		case kFacingNorthWest:
			if ((who->xVel > 0) && (who->zVel < 0))
			{
				if ((who->xPos > 0) && (who->zPos < 0))
					newDirection = kFacingNorthWest;
				else
					newDirection = kFacingSouthEast;
			}
			else
			{
				newDirection = kFacingNorthWest;
				if (NearTheEdge(who) && (who->xPos < 0) && (who->zPos > 0))
					DoPersonBallParted(who);			// shoot that ball! 
			}
			break;
		case kFacingSouthEast:
			if ((who->xVel > 0) && (who->zVel > 0))
			{
				if ((who->xPos < 0) && (who->zPos > 0))
					newDirection = kFacingSouthEast;
				else
					newDirection = kFacingNorthWest;
			}
			else
			{
				if (NearTheEdge(who) && (who->xPos > 0) && (who->zPos < 0))
					newDirection = kFacingNorthWest;
				else
					newDirection = kFacingSouthEast;
			}
			break;
		default:
			newDirection = kFacingSouthEast;
			break;
	}
	
	return (newDirection);
}


//========================================================  RunTheDiagonalSmarterNE  

short RunTheDiagonalSmarterNE (playerType *who)

{
	short			newDirection;
	
	switch (who->direction)
	{
		case kFacingNorthEast:
			if ((who->xVel < 0) && (who->zVel < 0))
			{
				if ((who->xPos < 0) && (who->zPos < 0))
					newDirection = kFacingNorthEast;
				else
					newDirection = kFacingSouthWest;
			}
			else
			{
				newDirection = kFacingNorthEast;
				if (NearTheEdgeSmarter(who) && (who->xPos > 0) && (who->zPos > 0) && 
						(DecentShot(who)))
					DoPersonBallParted(who);			// shoot that ball! 
			}
			break;
		case kFacingSouthWest:
			if ((who->xVel > 0) && (who->zVel > 0))
			{
				if ((who->xPos > 0) && (who->zPos > 0))
					newDirection = kFacingSouthWest;
				else
					newDirection = kFacingNorthEast;
			}
			else
			{
				if (NearTheEdgeSmarter(who) && (who->xPos < 0) && (who->zPos < 0))
					newDirection = kFacingNorthEast;
				else
					newDirection = kFacingSouthWest;
			}
			break;
		default:
			newDirection = kFacingSouthWest;
			break;
	}
	
	return (newDirection);
}


//========================================================  RunTheDiagonalSmarterNW  

short RunTheDiagonalSmarterNW (playerType *who)

{
	short			newDirection;
	
	switch (who->direction)
	{
		case kFacingNorthWest:
			if ((who->xVel > 0) && (who->zVel < 0))			// bugs in this code! 
			{
				if ((who->xPos > 0) && (who->zPos < 0))
					newDirection = kFacingNorthWest;
				else
					newDirection = kFacingSouthEast;
			}
			else
			{
				newDirection = kFacingNorthWest;
				if (NearTheEdgeSmarter(who) && (who->xPos < 0) && (who->zPos > 0) && 
						DecentShot(who))
					DoPersonBallParted(who);			// shoot that ball! 
			}
			break;
		case kFacingSouthEast:
			if ((who->xVel < 0) && (who->zVel > 0))
			{
				if ((who->xPos < 0) && (who->zPos > 0))
					newDirection = kFacingSouthEast;
				else
					newDirection = kFacingNorthWest;
			}
			else
			{
				if (NearTheEdgeSmarter(who) && (who->xPos > 0) && (who->zPos < 0))
					newDirection = kFacingNorthWest;
				else
					newDirection = kFacingSouthEast;
			}
			break;
		default:
			newDirection = kFacingSouthEast;
			break;
	}
	
	return (newDirection);
}


//========================================================  RunTheDiagonalSmartestNE  

short RunTheDiagonalSmartestNE (playerType *who)

{
	short			newDirection;
	
	switch (who->direction)
	{
		case kFacingNorthEast:
			if ((who->xVel < 0) && (who->zVel < 0))
			{
				if ((who->xPos < 0) && (who->zPos < 0))
					newDirection = kFacingNorthEast;
				else
					newDirection = kFacingSouthWest;
			}
			else
			{
				newDirection = kFacingNorthEast;
				if (NearTheEdgeSmarter(who) && (who->xPos > 0) && (who->zPos > 0) && 
						ExcellentShot(who))
					DoPersonBallParted(who);			// shoot that ball! 
			}
			break;
		case kFacingSouthWest:
			if ((who->xVel > 0) && (who->zVel > 0))
			{
				if ((who->xPos > 0) && (who->zPos > 0))
					newDirection = kFacingSouthWest;
				else
					newDirection = kFacingNorthEast;
			}
			else
			{
				if (NearTheEdgeSmarter(who) && (who->xPos < 0) && (who->zPos < 0))
					newDirection = kFacingNorthEast;
				else
					newDirection = kFacingSouthWest;
			}
			break;
		default:
			newDirection = kFacingSouthWest;
			break;
	}
	
	return (newDirection);
}


//========================================================  RunTheDiagonalSmartestNW  

short RunTheDiagonalSmartestNW (playerType *who)

{
	short			newDirection;
	
	switch (who->direction)
	{
		case kFacingNorthWest:
			if ((who->xVel > 0) && (who->zVel < 0))
			{
				if ((who->xPos > 0) && (who->zPos < 0))	// bugs in this function 
					newDirection = kFacingNorthWest;
				else
					newDirection = kFacingSouthEast;
			}
			else
			{
				newDirection = kFacingNorthWest;
				if (NearTheEdgeSmarter(who) && (who->xPos < 0) && (who->zPos > 0) && 
						ExcellentShot(who))
					DoPersonBallParted(who);			// shoot that ball! 
			}
			break;
		case kFacingSouthEast:
			if ((who->xVel < 0) && (who->zVel > 0))
			{
				if ((who->xPos < 0) && (who->zPos > 0))
					newDirection = kFacingSouthEast;
				else
					newDirection = kFacingNorthWest;
			}
			else
			{
				if (NearTheEdgeSmarter(who) && (who->xPos > 0) && (who->zPos < 0))
					newDirection = kFacingNorthWest;
				else
					newDirection = kFacingSouthEast;
			}
			break;
		default:
			newDirection = kFacingSouthEast;
			break;
	}
	
	return (newDirection);
}


//========================================================  SeekPlayerGoal  

short SeekPersonsGoal (playerType *who)

{
	#define kSmallArenaGoalXSRt	-10861
	#define kSmallArenaGoalXSLf	10861
	#define kSmallArenaGoalZS	10861
	#define kLargeArenaGoalXSRt	-13576
	#define kLargeArenaGoalXSLf	13576
	#define kLargeArenaGoalZS	13576
	short			newDirection;
	
	if (who->whichGoal == kRightGoal)
	{
		if (arenaSize == kLargeArena)
			newDirection = OctalToPoint(who, kLargeArenaGoalXSRt, kLargeArenaGoalZS);
		else
			newDirection = OctalToPoint(who, kSmallArenaGoalXSRt, kSmallArenaGoalZS);
	}
	else
	{
		if (arenaSize == kLargeArena)
			newDirection = OctalToPoint(who, kLargeArenaGoalXSLf, kLargeArenaGoalZS);
		else
			newDirection = OctalToPoint(who, kSmallArenaGoalXSLf, kSmallArenaGoalZS);
	}
	return (newDirection);
}


//========================================================  AnticipateBall  

short AnticipateBall (playerType *who, short framesAhead)

{
	register long	newX, newZ;
	short			newDirection;
	
	if (NearTheBall(who))
	{
		newDirection = OctalToPoint(who, theBall.xPos, theBall.zPos);
	}
	else
	{	
		newX = (long)theBall.xPos + (long)theBall.xVel / kVelocitySensitive * 
				(long)framesAhead;
		newZ = (long)theBall.zPos + (long)theBall.zVel / kVelocitySensitive * 
				(long)framesAhead;
			
		if (newX > kMaxShort)
			newX = kMaxShort;
		if (newX < kMinShort)
			newX = kMinShort;
		if (newZ > kMaxShort)
			newZ = kMaxShort;
		if (newZ < kMinShort)
			newZ = kMinShort;
		
		newDirection = OctalToPoint(who, (short)newX, (short)newZ);
	}
	
	return (newDirection);
}


//========================================================  AnticipatePersonsFlank  

short AnticipatePersonsFlank (playerType *who, short framesAhead)

{
	#define kFlankingDistXRt	-3072	// -48 pixels 
	#define kFlankingDistXLf	3072	// -48 pixels 
	#define kFlankingDistZ	3072	// 48 pixels 
	register long	newX, newZ;
	short			newDirection;
	
	if (who->selector == kOpponentSelector)
	{
		if (who->whichGoal == kRightGoal)
		{
			newX = (long)thePlayer.xPos + kFlankingDistXRt + (long)thePlayer.xVel / 
					kVelocitySensitive * (long)framesAhead;
		}
		else
		{
			newX = (long)thePlayer.xPos + kFlankingDistXLf + (long)thePlayer.xVel / 
					kVelocitySensitive * (long)framesAhead;
		}
		newZ = (long)thePlayer.zPos + kFlankingDistZ + (long)thePlayer.zVel / 
				kVelocitySensitive * (long)framesAhead;
	}
	else
	{
		if (who->whichGoal == kRightGoal)
		{
			newX = (long)theOpponent.xPos + kFlankingDistXRt + (long)theOpponent.xVel / 
					kVelocitySensitive * (long)framesAhead;
		}
		else
		{
			newX = (long)theOpponent.xPos + kFlankingDistXLf + (long)theOpponent.xVel / 
					kVelocitySensitive * (long)framesAhead;
		}
		newZ = (long)theOpponent.zPos + kFlankingDistZ + (long)theOpponent.zVel / 
				kVelocitySensitive * (long)framesAhead;
	}
	
	if (newX > kMaxShort)
		newX = kMaxShort;
	if (newX < kMinShort)
		newX = kMinShort;
	if (newZ > kMaxShort)
		newZ = kMaxShort;
	if (newZ < kMinShort)
		newZ = kMinShort;
	
	newDirection = OctalToPoint(who, (short)newX, (short)newZ);
	
	return (newDirection);
}


//========================================================  AnticipatePersonsFace  

short AnticipatePersonsFace (playerType *who, short framesAhead)

{
	register long	newX, newZ;
	short			newDirection;
	
	if (who->selector == kOpponentSelector)
	{
		newX = (long)thePlayer.xPos + (long)thePlayer.xVel / kVelocitySensitive * 
				(long)framesAhead;
		newZ = (long)thePlayer.zPos + (long)thePlayer.zVel / kVelocitySensitive * 
				(long)framesAhead;
	}
	else
	{
		newX = (long)theOpponent.xPos + (long)theOpponent.xVel / kVelocitySensitive * 
				(long)framesAhead;
		newZ = (long)theOpponent.zPos + (long)theOpponent.zVel / kVelocitySensitive * 
				(long)framesAhead;
	}
	
	if (newX > kMaxShort)
		newX = kMaxShort;
	if (newX < kMinShort)
		newX = kMinShort;
	if (newZ > kMaxShort)
		newZ = kMaxShort;
	if (newZ < kMinShort)
		newZ = kMinShort;
	
	newDirection = OctalToPoint(who, (short)newX, (short)newZ);
	
	return (newDirection);
}


//========================================================  RunTheCircleRight  

short RunTheCircleRight (playerType *who)

{
	short			newDirection;
	
	newDirection = who->direction;
	
	who->turnCount++;
	
	if ((who->turnCount == who->turnNow) || (PointingToDisaster(who)))
	{
		who->turnCount = 0;
		newDirection ++;
		if (newDirection > kLastDirection)
			newDirection -= (kLastDirection + 1);
	}
	
	if  ((!gameIsOver) && (NearTheGoal(who)) && 
			(ExcellentDirectionalShot(who, kFacingNorthEast)))
	{
		newDirection = kFacingNorthEast;
		who->direction = newDirection;
		DoPersonBallParted(who);		// shoot that ball! 
	}
	
	return (newDirection);
}


//========================================================  RunTheCircleLeft  

short RunTheCircleLeft (playerType *who)

{
	short			newDirection;
	
	newDirection = who->direction;
	
	who->turnCount++;
	
	if ((who->turnCount == who->turnNow) || (PointingToDisaster(who)))
	{
		who->turnCount = 0;
		newDirection --;
		if (newDirection < kFirstDirection)
			newDirection += (kLastDirection + 1);
	}
	
	if  ((NearTheGoal(who)) && (ExcellentDirectionalShot(who, kFacingNorthWest)))
	{
		newDirection = kFacingNorthWest;
		who->direction = newDirection;
		DoPersonBallParted(who);		// shoot that ball! 
	}
	
	return (newDirection);
}


//========================================================  BrakeIfLeaving  

void BrakeIfLeaving (playerType *who)

{
	long			xDelta, zDelta, newDistSquared;
	
	xDelta = (long)who->xPos - (long)theBall.xPos;
	zDelta = (long)who->zPos - (long)theBall.zPos;
	
	newDistSquared = (xDelta * xDelta) + (zDelta * zDelta);
	
	if (newDistSquared > oldDistSquared)
	{
		who->brakeApplied = TRUE;
	}
	
	oldDistSquared = newDistSquared;
}


//========================================================  BashUnlessLeaving  

void BashUnlessLeaving (playerType *who)

{
	#define			kMaximumRammingSpeed	25600000L
	long			xDelta, zDelta, velocitySquared, newDistSquared;
	
	xDelta = (long)thePlayer.xPos - (long)theOpponent.xPos;
	zDelta = (long)thePlayer.zPos - (long)theOpponent.zPos;
	
	velocitySquared = ((long)who->xVel * (long)who->xVel) + 
			((long)who->zVel * (long)who->zVel);
	
	newDistSquared = (xDelta * xDelta) + (zDelta * zDelta);
	
	if (newDistSquared < oldDistSquared)
	{
		if (velocitySquared < kMaximumRammingSpeed)
			who->bashApplied = TRUE;
	}
	else
		who->brakeApplied = TRUE;
	
	oldDistSquared = newDistSquared;
}


//========================================================  GeorgeDecides  

void GeorgeDecides (playerType *who)

{
	short			willCrouch;
	
	if (gameIsOver)
	{
		who->direction = RunTheCircleRight(who);
		LookUpBoardForce(who);
		return;
	}
	
	switch (whosGotBall)
	{
		case kBallIsNotHere:
			who->direction = kFacingRested;
			who->posture = kStanding;
			break;
		case kBallRollsFreely:
			if (((who->loopsBallHeld) == 0) || ((who->loopsBallHeld) > 60))
			{
				who->direction = DiagonalToBall(who);
				LookUpBoardForce(who);
				willCrouch = (short)(Ticks % 100);
				if ((NearTheBall(who)) && (willCrouch < kGeorgeCrouchPercent))
					who->posture = kCrouching;
				else
					who->posture = kStanding;
			}
			else
			{
				who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
				who->posture = kStanding;
			}
			break;
		case kPlayerHasBall:
			if (who->selector == kOpponentSelector)
			{
				who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
				who->posture = kStanding;
			}
			else
			{
				if (who->whichGoal == kRightGoal)
					who->direction = RunTheDiagonalNE(who);
				else
					who->direction = RunTheDiagonalNW(who);
				LookUpBoardForce(who);
				who->posture = kCarrying;
				
				if ((who->loopsBallHeld) < 60)
					DoPersonBallParted(who);			// shoot that ball! 
			}
			break;
		case kOpponentHasBall:
			if (who->selector == kOpponentSelector)
			{
				if (who->whichGoal == kRightGoal)
					who->direction = RunTheDiagonalNE(who);
				else
					who->direction = RunTheDiagonalNW(who);
				LookUpBoardForce(who);
				who->posture = kCarrying;
				
				if ((who->loopsBallHeld) < 60)
					DoPersonBallParted(who);			// shoot that ball! 
			}
			else
			{
				who->direction = OctalToPoint(who, theOpponent.xPos, theOpponent.zPos);
				who->posture = kStanding;
			}
			break;
	}
}


//========================================================  MaraDecides  

void MaraDecides (playerType *who)

{
	short			willCrouch;
	
	#if kDemoVersion
		return;
	#else
		if (gameIsOver)
		{
			who->direction = RunTheCircleRight(who);
			LookUpBoardForce(who);
			return;
		}
		
		switch (whosGotBall)
		{
			case kBallIsNotHere:
				if (who->selector == kOpponentSelector)
					who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
				else
					who->direction = OctalToPoint(who, theOpponent.xPos, theOpponent.zPos);
				who->posture = kStanding;
				break;
			case kBallRollsFreely:
				if (((who->loopsBallHeld) == 0) || ((who->loopsBallHeld) > 50))
				{
					who->direction = DiagonalToBall(who);
					LookUpBoardForce(who);
					willCrouch = (short)(Ticks % 100);
					if ((NearTheBall(who)) && (willCrouch < kMaraCrouchPercent))
						who->posture = kCrouching;
					else
						who->posture = kStanding;
				}
				else
				{
					who->direction = thePlayer.direction;
					who->posture = kStanding;
					LookUpBoardForce(who);
				}
				break;
			case kPlayerHasBall:
				if (who->selector == kOpponentSelector)
				{
					who->direction = thePlayer.direction;
					LookUpBoardForce(who);
					who->posture = kStanding;
				}
				else
				{
					if (who->whichGoal == kRightGoal)
						who->direction = RunTheDiagonalNE(who);
					else
						who->direction = RunTheDiagonalNW(who);
					LookUpBoardForce(who);
					who->posture = kCarrying;
					
					if ((who->loopsBallHeld) < 50)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				break;
			case kOpponentHasBall:
				if (who->selector == kOpponentSelector)
				{
					if (who->whichGoal == kRightGoal)
						who->direction = RunTheDiagonalNE(who);
					else
						who->direction = RunTheDiagonalNW(who);
					LookUpBoardForce(who);
					who->posture = kCarrying;
					
					if ((who->loopsBallHeld) < 50)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				else
				{
					who->direction = theOpponent.direction;
					LookUpBoardForce(who);
					who->posture = kStanding;
				}
				break;
		}
	#endif
}


//========================================================  OttoDecides  

void OttoDecides (playerType *who)

{
	short			willCrouch;
	Boolean			wasBraking;
	
	#if kDemoVersion
		return;
	#else
		if (gameIsOver)
		{
			who->direction = RunTheCircleRight(who);
			LookUpBoardForce(who);
			return;
		}
		
		wasBraking = who->brakeApplied;
		
		who->bashApplied = FALSE;
		who->brakeApplied = FALSE;
		
		switch (whosGotBall)
		{
			case kBallIsNotHere:
				if (who->selector == kOpponentSelector)
					who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
				else
					who->direction = OctalToPoint(who, theOpponent.xPos, theOpponent.zPos);
				who->posture = kStanding;
				break;
			case kBallRollsFreely:
				if (((who->loopsBallHeld) == 0) || ((who->loopsBallHeld) > 40))
				{
					who->direction = AnticipateBall(who, 6);
					LookUpBoardForce(who);
					willCrouch = (short)(Ticks % 100);
					if ((NearTheBall(who)) && (willCrouch < kOttoCrouchPercent))
						who->posture = kCrouching;
					else
						who->posture = kStanding;
					if (NearTheEdge(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				else
				{
					BashUnlessLeaving(who);
					who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
					LookUpBoardForce(who);
					if (NearTheEdge(who))
						who->direction = OctalToPoint(who, 0, 0);
					who->posture = kStanding;
				}
				break;
			case kPlayerHasBall:
				if (who->selector == kOpponentSelector)
				{
					BashUnlessLeaving(who);
					who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
					LookUpBoardForce(who);
					who->posture = kStanding;
					if (NearTheEdge(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				else
				{
					if (who->whichGoal == kRightGoal)
						who->direction = RunTheDiagonalSmarterNE(who);
					else
						who->direction = RunTheDiagonalSmarterNW(who);
					LookUpBoardForce(who);
					who->posture = kCarrying;
					
					if ((who->loopsBallHeld) < 40)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				break;
			case kOpponentHasBall:
				if (who->selector == kOpponentSelector)
				{
					if (who->whichGoal == kRightGoal)
						who->direction = RunTheDiagonalSmarterNE(who);
					else
						who->direction = RunTheDiagonalSmarterNW(who);
					LookUpBoardForce(who);
					who->posture = kCarrying;
					
					if ((who->loopsBallHeld) < 40)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				else
				{
					BashUnlessLeaving(who);
					who->direction = OctalToPoint(who, theOpponent.xPos, theOpponent.zPos);
					LookUpBoardForce(who);
					who->posture = kStanding;
					if (NearTheEdge(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				break;
		}
		
		if (!wasBraking && who->brakeApplied)
			PlaySoundSMS(kBrakeSound);
	#endif
}


//========================================================  ClaireDecides  

void ClaireDecides (playerType *who)

{
	short			willCrouch;
	
	#if kDemoVersion
		return;
	#else
		if (gameIsOver)
		{
			who->direction = RunTheCircleRight(who);
			LookUpBoardForce(who);
			return;
		}
		
		switch (whosGotBall)
		{
			case kBallIsNotHere:
				who->posture = kStanding;
				if (who->selector == kOpponentSelector)
					who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
				else
					who->direction = OctalToPoint(who, theOpponent.xPos, theOpponent.zPos);
				break;
			case kBallRollsFreely:
				if (((who->loopsBallHeld) == 0) || ((who->loopsBallHeld) > 30))
				{
					willCrouch = (short)(Ticks % 100);
					if ((NearTheBall(who)) && (willCrouch < kClaireCrouchPercent))
						who->posture = kCrouching;
					else
						who->posture = kStanding;
					who->direction = AnticipateBall(who, 8);
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				else
				{
					who->direction = AnticipatePersonsFlank(who, 8);
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
					who->posture = kStanding;
				}
				break;
			case kPlayerHasBall:
				if (who->selector == kOpponentSelector)
				{
					who->posture = kStanding;
					who->direction = AnticipatePersonsFlank(who, 8);
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				else
				{
					who->posture = kCarrying;
					if (who->whichGoal == kRightGoal)
						who->direction = RunTheCircleRight(who);
					else
						who->direction = RunTheCircleLeft(who);
					LookUpBoardForce(who);
					
					if ((who->loopsBallHeld) < 30)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				break;
			case kOpponentHasBall:
				if (who->selector == kOpponentSelector)
				{
					who->posture = kCarrying;
					if (who->whichGoal == kRightGoal)
						who->direction = RunTheCircleRight(who);
					else
						who->direction = RunTheCircleLeft(who);
					LookUpBoardForce(who);
					
					if ((who->loopsBallHeld) < 30)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				else
				{
					who->posture = kStanding;
					who->direction = AnticipatePersonsFlank(who, 8);
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				break;
		}
	#endif
}


//========================================================  EazeDecides  

void EazeDecides (playerType *who)

{
	short			willCrouch;
	Boolean			wasBraking;
	
	#if kDemoVersion
		return;
	#else
		if (gameIsOver)
		{
			who->direction = RunTheCircleRight(who);
			LookUpBoardForce(who);
			return;
		}
		
		wasBraking = who->brakeApplied;
		
		who->bashApplied = FALSE;
		who->brakeApplied = FALSE;
		
		switch (whosGotBall)
		{
			case kBallIsNotHere:
				who->posture = kStanding;
				if (who->selector == kOpponentSelector)
					who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
				else
					who->direction = OctalToPoint(who, theOpponent.xPos, theOpponent.zPos);
				break;
			case kBallRollsFreely:
				if (((who->loopsBallHeld) == 0) || ((who->loopsBallHeld) > 20))
				{
					willCrouch = (short)(Ticks % 100);
					if ((NearTheBall(who)) && (willCrouch < kEazeCrouchPercent))
					{
						BrakeIfLeaving(who);
						who->posture = kCrouching;
					}
					else
						who->posture = kStanding;
					who->direction = AnticipateBall(who, 12);
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				else
				{
					if (NearPerson())
						who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
					else
						who->direction = AnticipatePersonsFace(who, 12);
					if (NearTheEdgeSmarter(who))
					{
						BrakeIfLeaving(who);
						who->direction = OctalToPoint(who, 0, 0);
					}
					LookUpBoardForce(who);
					who->posture = kStanding;
				}
				break;
			case kPlayerHasBall:
				if (who->selector == kOpponentSelector)
				{
					who->posture = kStanding;
					if (NearPerson())
						who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
					else
						who->direction = AnticipatePersonsFace(who, 12);
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				else
				{
					who->posture = kCarrying;
					if (who->strategy == kRunDiagonal)
					{
						if (who->whichGoal == kRightGoal)
							who->direction = RunTheDiagonalSmartestNE(who);
						else
							who->direction = RunTheDiagonalSmartestNW(who);
					}
					else
					{
						if (who->whichGoal == kRightGoal)
							who->direction = RunTheCircleRight(who);
						else
							who->direction = RunTheCircleLeft(who);
					}
					LookUpBoardForce(who);
					
					if ((who->loopsBallHeld) < 20)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				break;
			case kOpponentHasBall:
				if (who->selector == kOpponentSelector)
				{
					who->posture = kCarrying;
					if (who->strategy == kRunDiagonal)
					{
						if (who->whichGoal == kRightGoal)
							who->direction = RunTheDiagonalSmartestNE(who);
						else
							who->direction = RunTheDiagonalSmartestNW(who);
					}
					else
					{
						if (who->whichGoal == kRightGoal)
							who->direction = RunTheCircleRight(who);
						else
							who->direction = RunTheCircleLeft(who);
					}
					LookUpBoardForce(who);
					
					if ((who->loopsBallHeld) < 20)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				else
				{
					who->posture = kStanding;
					if (NearPerson())
						who->direction = OctalToPoint(who, theOpponent.xPos, theOpponent.zPos);
					else
						who->direction = AnticipatePersonsFace(who, 12);
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				break;
		}
		
		if (!wasBraking && who->brakeApplied)
			PlaySoundSMS(kBrakeSound);
	#endif
}


//========================================================  TeakDecides  

void TeakDecides (playerType *who)

{
	short			willCrouch;
	Boolean			wasBraking;
	
	#if kDemoVersion
		return;
	#else
		if (gameIsOver)
		{
			who->direction = RunTheCircleRight(who);
			LookUpBoardForce(who);
			return;
		}
		
		wasBraking = who->brakeApplied;
		
		who->bashApplied = FALSE;
		who->brakeApplied = FALSE;
		
		switch (whosGotBall)
		{
			case kBallIsNotHere:
				who->posture = kStanding;
				if (who->selector == kOpponentSelector)
					who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
				else
					who->direction = OctalToPoint(who, theOpponent.xPos, theOpponent.zPos);
				break;
			case kBallRollsFreely:
				if (((who->loopsBallHeld) == 0) || ((who->loopsBallHeld) > 10))
				{
					BrakeIfLeaving(who);
					willCrouch = (short)(Ticks % 100);
					if ((NearTheBall(who)) && (willCrouch < kTeakCrouchPercent))
					{
						who->posture = kCrouching;
					}
					else
					{
						who->posture = kStanding;
					}
					who->direction = AnticipateBall(who, 16);
					if (NearTheEdgeSmarter(who))
					{
						who->direction = OctalToPoint(who, 0, 0);
						who->brakeApplied = TRUE;
					}
					LookUpBoardForce(who);
				}
				else
				{
					if (NearPerson())
					{
						BashUnlessLeaving(who);
						who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
					}
					else
					{
						who->direction = AnticipatePersonsFace(who, 16);
					}
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
					who->posture = kStanding;
				}
				break;
			case kPlayerHasBall:
				if (who->selector == kOpponentSelector)
				{
					who->posture = kStanding;
					if (NearPerson())
					{
						BashUnlessLeaving(who);
						who->direction = OctalToPoint(who, thePlayer.xPos, thePlayer.zPos);
					}
					else
					{
						who->direction = AnticipatePersonsFace(who, 16);
					}
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				else
				{
					who->posture = kCarrying;
					switch (who->strategy)
					{
						case kRunDiagonal:
							if (who->whichGoal == kRightGoal)
								who->direction = RunTheDiagonalSmartestNE(who);
							else
								who->direction = RunTheDiagonalSmartestNW(who);
							break;
						case kRunCircle:
							if (who->whichGoal == kRightGoal)
								who->direction = RunTheCircleRight(who);
							else
								who->direction = RunTheCircleLeft(who);
							break;
					}
					LookUpBoardForce(who);
					
					if ((who->loopsBallHeld) < 10)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				break;
			case kOpponentHasBall:
				if (who->selector == kOpponentSelector)
				{
					who->posture = kCarrying;
					switch (who->strategy)
					{
						case kRunDiagonal:
							if (who->whichGoal == kRightGoal)
								who->direction = RunTheDiagonalSmartestNE(who);
							else
								who->direction = RunTheDiagonalSmartestNW(who);
							break;
						case kRunCircle:
							if (who->whichGoal == kRightGoal)
								who->direction = RunTheCircleRight(who);
							else
								who->direction = RunTheCircleLeft(who);
							break;
					}
					LookUpBoardForce(who);
					
					if ((who->loopsBallHeld) < 10)
						DoPersonBallParted(who);			// shoot that ball! 
				}
				else
				{
					who->posture = kStanding;
					if (NearPerson())
					{
						BashUnlessLeaving(who);
						who->direction = OctalToPoint(who, theOpponent.xPos, theOpponent.zPos);
					}
					else
					{
						who->direction = AnticipatePersonsFace(who, 16);
					}
					LookUpBoardForce(who);
					if (NearTheEdgeSmarter(who))
						who->direction = OctalToPoint(who, 0, 0);
				}
				break;
		}
		
		if (!wasBraking && who->brakeApplied)
			PlaySoundSMS(kBrakeSound);
	#endif
}


