//============================================================
//============================================================
//==														==
//==				Game Dynamics Routines					==
//==														==
//============================================================
//============================================================


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

#include "Globals.h"
#include "UnivUtilities.h"
#include "Dynamics.h"
#include "SoundUtils.h"
#include "Render.h"
#include "Ball.h"


//========================================================  Functions  
//========================================================  DoPersonBallMerged  

void DoPersonBallMerged (playerType *who)

{
	register long	xMomentum, zMomentum;
	short			strategum;
	
	PlaySoundSMS(kBallPickUpSound);
											// calc. components of momentum 
	xMomentum = (who->xVel * kPersonMass) + (theBall.xVel * kBallMass);
	zMomentum = (who->zVel * kPersonMass) + (theBall.zVel * kBallMass);
											// momentum / mass = velocity 
	who->xVel = xMomentum / kPersonBallMass;
	who->zVel = zMomentum / kPersonBallMass;
	
	if ((who->persona == kHumanPlayer) || (who->persona == kNetHuman))
	{
		who->mouseWasLetUp = FALSE;
	}
	else
	{
		if (who->persona == kMisterEaze)
		{
			who->strategy = (short)RandomCoin();
		}
		else if (who->persona == kMissTeak)
		{
			strategum = RandomInt(100);
			if (strategum < who->teaksThresh)
				who->strategy = kRunDiagonal;
			else
				who->strategy = kRunCircle;
		}
	}
				
	theBall.eraseTheBall = TRUE;
	drawThisFrame = TRUE;
	who->posture = kCarrying;
	theBall.mode = kBallHeld;
	if (who->selector == kPlayerSelector)
	{
		whosGotBall = kPlayerHasBall;
		theBall.modifier = kPlayerHolding;
		theOpponent.loopsBallHeld = 0;			// zero out the other's timer 
		if (thePlayer.loopsBallHeld == 0)		// ??? 
			thePlayer.loopsBallHeld = kLoopLimitOnHeldBall;
	}
	else
	{
		whosGotBall = kOpponentHasBall;
		theBall.modifier = kOpponentHolding;
		thePlayer.loopsBallHeld = 0;
		if (theOpponent.loopsBallHeld == 0)
			theOpponent.loopsBallHeld = kLoopLimitOnHeldBall;
	}
	
	UpdateBallTimers(&thePlayer);
	UpdateBallTimers(&theOpponent);
	UpdateArrows();
}


//========================================================  DoPersonBallParted  

void DoPersonBallParted (playerType *who)

{
	#define			kBallDiskDistance	1216
	#define			kOffsetMultiplier	2
	short			deltaPX, deltaPZ;
	
	PlaySoundSMS(kBallDropSound);
	
	deltaPX = boardForceTable[who->direction][kXComponent] * kPersonImpulse;
	deltaPZ = boardForceTable[who->direction][kZComponent] * kPersonImpulse;
	
	theBall.xVel = who->xVel + (deltaPX / kBallMass);			// momentum => velocity 
	theBall.zVel = who->zVel + (deltaPZ / kBallMass);			// x & z components 
	theBall.xPos = who->xPos + (deltaPX * kOffsetMultiplier);	// get ball clearÉ 
	theBall.zPos = who->zPos + (deltaPZ * kOffsetMultiplier);	// of disk. 
	
	BallRectFromPosition();
	
	who->xVel -= deltaPX / kPersonMass;
	who->zVel -= deltaPZ / kPersonMass;
	
	who->posture = kCrouching;
	theBall.mode = kBallRolling;
	whosGotBall = kBallRollsFreely;
	
	if (who->selector == kPlayerSelector)
	{
		theBall.modifier = kPlayerLastHeld;
	}
	else
	{
		theBall.modifier = kOpponentLastHeld;
	}
	
	if (thePlayer.persona == kMissTeak)
	{
		oldDistSquared = ((long)(thePlayer.xPos) * (long)(thePlayer.xPos)) + 
				((long)(thePlayer.zPos) * (long)(thePlayer.zPos));
	}
	else if (theOpponent.persona == kMissTeak)
	{
		oldDistSquared = ((long)(theOpponent.xPos) * (long)(theOpponent.xPos)) + 
				((long)(theOpponent.zPos) * (long)(theOpponent.zPos));
	}
	
	if ((who->persona == kHumanPlayer) || (who->persona == kNetHuman))
		who->mouseWasLetUp = FALSE;
	
	UpdateArrows();
}


//========================================================  DoPersonBallCollided  

void DoPersonBallCollided (playerType *who)

{
	#define		kPBLimitScale	512L			// 1024 occ. probs, 2048 more probs 
	short		original2XVel, original2ZVel;
	long		distX, distZ;
	long		new1XVel, new1ZVel, new2XVel, new2ZVel;
	long		distSquared, scalar1, scalar2;
	
	who->xPos -= (who->xVel / kVelocitySensitive);
	who->zPos -= (who->zVel / kVelocitySensitive);
	
	theBall.xVel -= theBall.xVel / kPBEnergyAbsorbed;
	theBall.zVel -= theBall.zVel / kPBEnergyAbsorbed;
	who->xVel -= who->xVel / kPBEnergyAbsorbed;
	who->zVel -= who->zVel / kPBEnergyAbsorbed;
	
	theBall.xVel /= kPersonSmallMass;
	theBall.zVel /= kPersonSmallMass;
	who->xVel /= kBallMass;
	who->zVel /= kBallMass;
	
	original2XVel = who->xVel;
	original2ZVel = who->zVel;;
	
	theBall.xVel -= original2XVel;
	theBall.zVel -= original2ZVel;
	who->xVel = 0;
	who->zVel = 0;
	
	distX = (long)who->xPos - (long)theBall.xPos;
	distZ = (long)who->zPos - (long)theBall.zPos;
	
	distSquared = ((distX * distX) + (distZ * distZ)) / kPBLimitScale;
	scalar1 = (((long)-theBall.xVel * distZ) + ((long)theBall.zVel * distX)) / kPBLimitScale;
	scalar2 = (((long)theBall.xVel * distX) + ((long)theBall.zVel * distZ)) / kPBLimitScale;
	
	if (distSquared != 0)
	{
		new1XVel = -scalar1 * distZ / distSquared;
		new1ZVel = scalar1 * distX / distSquared;
		new2XVel = scalar2 * distX / distSquared;
		new2ZVel = scalar2 * distZ / distSquared;
	}
	
	if (theBall.justHitWall == 0)
	{
		theBall.xVel = (short)new1XVel + original2XVel;
		theBall.zVel = (short)new1ZVel + original2ZVel;
	}
	
	who->xVel = (short)new2XVel + original2XVel;
	who->zVel = (short)new2ZVel + original2ZVel;
	
	theBall.xVel *= kPersonSmallMass;
	theBall.zVel *= kPersonSmallMass;
	who->xVel *= kBallMass;
	who->zVel *= kBallMass;
}


//========================================================  DoPersonPersonCollided  

void DoPersonPersonCollided (void)

{
	#define		kLimitScale	1024L
	short		original2XVel, original2ZVel;
	long		distX, distZ;
	long		new1XVel, new1ZVel, new2XVel, new2ZVel;
	long		distSquared, scalar1, scalar2;
	
	PlaySoundSMS(kClashSound);
	thePlayer.justHitOpponent = kLoopsImpactless;
	theOpponent.justHitOpponent = kLoopsImpactless;
	
	thePlayer.justHitWall = 0;
	theOpponent.justHitWall = 0;
	
	thePlayer.xVel -= thePlayer.xVel / kEnergyAbsorbed;
	thePlayer.zVel -= thePlayer.zVel / kEnergyAbsorbed;
	theOpponent.xVel -= theOpponent.xVel / kEnergyAbsorbed;
	theOpponent.zVel -= theOpponent.zVel / kEnergyAbsorbed;
	
	original2XVel = theOpponent.xVel;
	original2ZVel = theOpponent.zVel;;
	
	thePlayer.xVel -= original2XVel;
	thePlayer.zVel -= original2ZVel;
	theOpponent.xVel = 0;
	theOpponent.zVel = 0;
	
	distX = (long)theOpponent.xPos - (long)thePlayer.xPos;
	distZ = (long)theOpponent.zPos - (long)thePlayer.zPos;
	
	distSquared = ((distX * distX) + (distZ * distZ)) / kLimitScale;
	scalar1 = (((long)-thePlayer.xVel * distZ) + ((long)thePlayer.zVel * distX)) / kLimitScale;
	scalar2 = (((long)thePlayer.xVel * distX) + ((long)thePlayer.zVel * distZ)) / kLimitScale;
	
	if (distSquared != 0)
	{
		new1XVel = -scalar1 * distZ / distSquared;
		new1ZVel = scalar1 * distX / distSquared;
		new2XVel = scalar2 * distX / distSquared;
		new2ZVel = scalar2 * distZ / distSquared;
	}
	
	thePlayer.xVel = (short)new1XVel + original2XVel;
	thePlayer.zVel = (short)new1ZVel + original2ZVel;
	theOpponent.xVel = (short)new2XVel + original2XVel;
	theOpponent.zVel = (short)new2ZVel + original2ZVel;
}


//========================================================  CheckPersonBallCollision  

void CheckPersonBallCollision (playerType *who)

{
	#define		kSqrImpactDistancePB	1478656L	// r. of ball = 416, r. disk = 800 
	long		sqrXDistance, sqrDistance;			// Add these & square the result. 
	
	if ((who->mode != kInArena) || (theBall.mode != kBallRolling) || 
			(who->justHitBall != 0))
	{
		return;
	}
	
	sqrXDistance = ((long)theBall.xPos - who->xPos) * 
			((long)theBall.xPos - who->xPos);
	
	if (sqrXDistance < kSqrImpactDistancePB)		// check x 1st (one mult. only) 
	{
		sqrDistance = sqrXDistance + ((long)theBall.zPos - who->zPos) * 
				((long)theBall.zPos - who->zPos);
		
		if (sqrDistance < kSqrImpactDistancePB)
		{
			if ((who->posture == kCrouching) && (who->mouseWasLetUp))
			{
				DoPersonBallMerged(who);
			}
			else
			{
				PlaySoundSMS(kClashSound);
				
				DoPersonBallCollided(who);
				who->justHitBall = kLoopsImpactless;
				CheckUpOnBall();
				
				if (who->selector == kPlayerSelector)
				{
					theBall.modifier = kPlayerLastHeld;
				}
				else
				{
					theBall.modifier = kOpponentLastHeld;
				}
			}
		}
	}
}


//========================================================  CheckPersonPersonCollision  

void CheckPersonPersonCollision (void)

{
	#define	kSqrImpactDistancePO	2560000L	// 2 * r. disk = 800 
	long			sqrXDistance, sqrDistance;	// Add these & square the result. 
	
	if ((thePlayer.mode != kInArena) || (theOpponent.mode != kInArena) || 
			(thePlayer.justHitOpponent != 0))
	{
		return;
	}
	
	sqrXDistance = ((long)theOpponent.xPos - thePlayer.xPos) * 
			((long)theOpponent.xPos - thePlayer.xPos);
	
	if (sqrXDistance < kSqrImpactDistancePO)	// check x first (one multiply only) 
	{
		sqrDistance = sqrXDistance + ((long)theOpponent.zPos - thePlayer.zPos) * 
				((long)theOpponent.zPos - thePlayer.zPos);
		
		if (sqrDistance < kSqrImpactDistancePO)
		{
			DoPersonPersonCollided();
			
			do
			{
				thePlayer.xPos += (thePlayer.xVel / kVelocitySensitive);
				thePlayer.zPos += (thePlayer.zVel / kVelocitySensitive);
				theOpponent.xPos += (theOpponent.xVel / kVelocitySensitive);
				theOpponent.zPos += (theOpponent.zVel / kVelocitySensitive);
				
				sqrXDistance = ((long)theOpponent.xPos - thePlayer.xPos) * 
						((long)theOpponent.xPos - thePlayer.xPos);
				sqrDistance = sqrXDistance + ((long)theOpponent.zPos - thePlayer.zPos) * 
						((long)theOpponent.zPos - thePlayer.zPos);
				
			} while (sqrDistance < kSqrImpactDistancePO);
		}
	}
}


//========================================================  GetPlaceOnArena  

short GetPlaceOnArena (short xPos, short zPos)

{
	register short	indexX, indexZ;
	short			signX, signZ;
	short			forceX, forceZ;
	
	indexX = xPos;
	if (indexX < 0)
	{
		indexX = -indexX;
		signX = -1;
	}
	else
	{
		signX = 1;
	}
	indexX /= 512;
	
	indexZ = zPos;
	if (indexZ < 0)
	{
		indexZ = -indexZ;
		signZ = -1;
	}
	else
	{
		signZ = 1;
	}
	indexZ /= 512;
	
	if (xPos < 0)
	{
		switch (leftGoalLeague)
		{
			case kLittleLeague:
				forceX = *(littleForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			case kJuniorVarsity:
				forceX = *(juniorForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			case kVarsity:
				forceX = *(varsityForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			case kMinorLeague:
				forceX = *(minorForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			case kProfessional:
				forceX = *(proForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			default:
				forceX = 0;
				break;
		}
	}
	else
	{
		switch (rightGoalLeague)
		{
			case kLittleLeague:
				forceX = *(littleForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			case kJuniorVarsity:
				forceX = *(juniorForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			case kVarsity:
				forceX = *(varsityForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			case kMinorLeague:
				forceX = *(minorForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			case kProfessional:
				forceX = *(proForceTable + indexX * 82 + indexZ * 2 + kXComponent);
				break;
			default:
				forceX = 0;
				break;
		}
	}

	return (forceX);
}


//========================================================  HandlePersonWallCollision  

void HandlePersonWallCollision (playerType *who)

{
	short			tempVel, wheresIt;
	
	if (who->justHitWall != 0)
	{
		who->flag = kIsNormal;
		return;
	}
	
	if (who->xPos < 0)
	{
		if (who->xVel > who->zVel)
			return;
	}
	else
	{
		if (-who->xVel > who->zVel)
			return;
	}
	
	PlaySoundSMS(kRicochetSound);
	who->flag = kIsRebounding;
	who->justHitWall = kLoopsImpactless;
	
	who->xPos -= (who->xVel / kVelocitySensitive);
	who->zPos -= (who->zVel / kVelocitySensitive);
	
	if (who->xPos < 0)
	{
		tempVel = who->xVel;
		who->xVel = who->zVel;
		who->zVel = tempVel;
	}
	else
	{
		tempVel = who->xVel;
		who->xVel = -who->zVel;
		who->zVel = -tempVel;
	}
	
	wheresIt = GetPlaceOnArena(who->xPos, who->zPos);
	if ((wheresIt == kBackBoard) || (wheresIt == kGoalPath))
	{
		if (who->zVel > 0)
			who->zVel *= -1;
		
		if (who->xPos < 0)
		{
			if (who->xVel < 0)
				who->xVel *= -1;
		}
		else
		{
			if (who->xVel > 0)
				who->xVel *= -1;
		}
	}
}


//========================================================  CheckUpOnBall  

void CheckUpOnBall (void)

{
	short		wheresIt;
	
	wheresIt = GetPlaceOnArena(theBall.xPos, theBall.zPos);
	if (wheresIt == kBackBoard)
	{
		if (theBall.zVel > 0)
			theBall.zVel *= -1;
		
		if (theBall.xPos < 0)
		{
			if (theBall.xVel < 0)
				theBall.xVel *= -1;
		}
		else
		{
			if (theBall.xVel > 0)
				theBall.xVel *= -1;
		}
	}
}


//========================================================  HandleBallWallCollision  

void HandleBallWallCollision (void)

{
	short			tempVel;
	
	if (theBall.justHitWall != 0)
	{
		theBall.flag = kIsNormal;
		return;
	}
	
	if (theBall.xPos < 0)
	{
		if (theBall.xVel > theBall.zVel)
			return;
	}
	else
	{
		if (-theBall.xVel > theBall.zVel)
			return;
	}
	
	PlaySoundSMS(kRicochetSound);
	theBall.flag = kIsRebounding;
	
	theBall.justHitWall = kLoopsImpactless;
	thePlayer.justHitBall = 0;
	theOpponent.justHitBall = 0;
	
	theBall.xPos -= (theBall.xVel / kVelocitySensitive);
	theBall.zPos -= (theBall.zVel / kVelocitySensitive);
	
	if (theBall.xPos < 0)
	{
		tempVel = theBall.xVel;
		theBall.xVel = theBall.zVel;
		theBall.zVel = tempVel;
	}
	else
	{
		tempVel = theBall.xVel;
		theBall.xVel = -theBall.zVel;
		theBall.zVel = -tempVel;
	}
	
	CheckUpOnBall();
}


//========================================================  HandleCollisions  

void HandleCollisions (void)

{
	if (netGameInSession)
	{
		if (imTheMaster)
		{
			CheckPersonBallCollision(&thePlayer);
			CheckPersonBallCollision(&theOpponent);
		}
		else
		{
			CheckPersonBallCollision(&theOpponent);
			CheckPersonBallCollision(&thePlayer);
		}
	}
	else
	{
		CheckPersonBallCollision(&thePlayer);
		CheckPersonBallCollision(&theOpponent);
	}
	
	CheckPersonPersonCollision();
	
	if (thePlayer.justHitWall > 0)
	{
		thePlayer.justHitWall--;
		if (thePlayer.justHitWall == kFrameToDampen)
		{
			thePlayer.xVel -= thePlayer.xVel / kPersonDampening;
			thePlayer.zVel -= thePlayer.zVel / kPersonDampening;
		}
	}
	if (thePlayer.justHitBall > 0)
		thePlayer.justHitBall--;
	if (thePlayer.justHitOpponent > 0)
		thePlayer.justHitOpponent--;
	
	if (theOpponent.justHitWall > 0)
	{
		theOpponent.justHitWall--;
		if (theOpponent.justHitWall == kFrameToDampen)
		{
			theOpponent.xVel -= theOpponent.xVel / kPersonDampening;
			theOpponent.zVel -= theOpponent.zVel / kPersonDampening;
		}
	}
	if (theOpponent.justHitBall > 0)
		theOpponent.justHitBall--;
	if (theOpponent.justHitOpponent > 0)
		theOpponent.justHitOpponent--;
	
	if (theBall.justHitWall > 0)
	{
		theBall.justHitWall--;
		if (theBall.justHitWall == kFrameToDampen)
		{
			theBall.xVel /= kBallDampening;
			theBall.zVel /= kBallDampening;
		}
	}
}

