/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

/*
 * includes
 */

#include "ult_64.h"

#include "main.h"
#include "overgfx.h"
#include "ft.h"
#include "camera.h"
#include "cammodes.h"


/*
 * defines
 */

#define ABS(x)			(((x)<0)?(-(x)):(x))

/*
 * typedefs
 */

extern int
	camFirstFrame;

/*
 * structures
 */

/*
 * function prototypes
 */


extern void CamClamp(signed long *result,signed long min,signed long max);

int  RayProject(long *src,long *dest,long *res);

void CamEvaluateCircle(camPos *cur,camPos *ideal,playert *target,int flags);


void CamEvaluateSideline(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateSideline2(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateOverhead(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateSky(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateSky2(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluatePress(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateClassic(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateBehind(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateEnd(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateReplay(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateSpecial(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluate3PtContest(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateFaceKB(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateFaceSO(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateFaceSP(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateDunkAI(camPos *cur,camPos *ideal,playert *target,int flags);
void CamEvaluateScore(camPos *cur);

/*
 * globals
 */

void		(*camModeFunctions[])(camPos *,camPos *,playert *,int)=
{
	CamEvaluatePress,
	CamEvaluateSideline,
	CamEvaluateSideline2,
	CamEvaluateOverhead,
	CamEvaluateSky,
	CamEvaluateSky2,
	CamEvaluateClassic,
	CamEvaluateBehind,
	CamEvaluateEnd,
	CamEvaluateFaceKB,
	CamEvaluateFaceSO,
	CamEvaluateFaceSP,
	CamEvaluateDunkAI,
	CamEvaluateReplay,
	CamEvaluateSpecial,
	CamEvaluate3PtContest,
	NULL
};

char		*camModeNames[]=
{
	"Press",
	"Sideline",
	"Sideline2",
	"Overhead",
	"Sky",
	"Sky2",
	"Classic",
	"Behind",
	"End",
	"FaceKB",
	"FaceSO",
	"FaceSP",
	"DunkAI",
	"Replay",
	"Special",
	"3Pt Contest",
	NULL
};

specialDef specialDefs[]=
{
	{	"DribbleI",	-7000,	5560,	1500,	1536,	-16384,	SPECIAL_STATIC	},
	{	"Backboard",	0,	14403,	3781,	5120,	32768,	SPECIAL_STATIC	},
	{	"Backboard2",	0,	16500,	5000,	5120,	32768,	SPECIAL_STATIC	},
	{	"Basket",	0, (BASKET_Y>>10)+128, (BASKET_Z>>10)+1000, 5120, 32768, SPECIAL_STATIC },
	{	"Baseline",	1057,	16626,	2397,	512,	162816,	SPECIAL_XFLIP | SPECIAL_STATIC	},
	{	"Baseline2",	2000,	18000,	2397,	512,	30000,	SPECIAL_XFLIP | SPECIAL_STATIC	},
	{	"Cheer",	9274,	12496,	2110,	0,	147456,	SPECIAL_XFLIP | SPECIAL_STATIC	},
	{	"Overhead",	-292,	9463,	7218,	9120,	195672,	SPECIAL_STATIC	},
	{	"Overhead2",	-120,	7912,	10107,	11120,	131072,	SPECIAL_STATIC	},
	{	"SideHoop",	-3323,	12914,	3968,	2048,	114176,	SPECIAL_STATIC	},
	{	"InYourFace",	0,	-3000,	3000,	4000,	0,	SPECIAL_STATIC | SPECIAL_NOYFLIP	},
	{	"Shoulder1",	500,	-2000,	2000,	4000,	0,	0		},
	{	"Shoulder2",	-500,	-2000,	2000,	4000,	0,	0		},
	{	"Shoulder3",	0,	-2000,	2000,	4000,	0,	0		},
	{	"FreeThrow",	0, 10000,	1536,	-1024,	32768,	SPECIAL_STATIC	},
	{	"UpChest",	1000,	3000,	-500,	0,	0,	0		},
	{	"DribbleK",	-7000,	4000,	1300,	1536,	-16384,	SPECIAL_STATIC	},
	{	"FaceVH",	-400,	+10000,	1500,	-1024,	0,	SPECIAL_STATIC	},
	{	"Intro4",	-3000,	-6000,	2000,	4000,	0,	0		},
	{	"Circle",	-3000,	-6000,	2000,	4000,	0,	0		},
	{	"FaceAI",	4000,	0,	4000,	0,	0,	0		},
	{	"FreeThrow",	0,	2500,	2150,	1024,	0,	SPECIAL_STATIC	},
	{	"FreeThrow",	0,	2500,	2150,	1024,	0,	SPECIAL_STATIC	},

	{	"Face1",	0,	2000,	2000,	-1024,	32768,	SPECIAL_STATIC	},
	{	"New",	-6819,	-8916, 2441,	-23552,	1536,	0	},
	{	"New",	-2371,	4749,	-16382,	-2560,	5632,	SPECIAL_STATIC	},
	{	"New",	-4087,	-4205,	-5567,	-27648,	3584,	SPECIAL_STATIC	},
	{	"New",	-9376,	1500,	-11972,	-16896,	0,	SPECIAL_STATIC	},
	{	"New",	148,	3526,	-3528,	32256,	2048,	SPECIAL_STATIC	},
	{	"New",	2377,	3572,	-11264,	23040,	2048,	SPECIAL_STATIC	},
	{	"New",	-10521,	3178,	-14762,	-11744,	1000,	SPECIAL_STATIC	},
	{	"New",	-1153,	3196,	1825,	-32224,	512,	SPECIAL_STATIC	},
	{	"New",	12145,	3550,	1542,	24092,	0,	SPECIAL_STATIC	},

	{	NULL,		0,	0,	0,	0,	0,	0		},
};


int		camActionX=0,camActionY=0,camActionAngle=0,camActionPlayer=0;
int		camActionBasket;

int		camSpecialTargetNo;
int		camSpecialMode;
int		camSpecialCut;
int		camSpecialCutOnReturn;
int		camSpecialFirstTime;

static int		camSpecialXFlip;
static int		camSpecialRotate;

extern int	camBasketFactor;

/*
 * code
 */


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateSideline(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		ph;
	int		offset;

	// get target height
	if(flags & CAM_FLGTARGETBALL)	ph=(target->flip_zco<<4) + (1000<<14);
	else				ph=2000<<14;

	// set values
	ideal->theta = -16384;

	ideal->x = (target->flip_xco<<4)-(10000<<14);
	CamClamp(&ideal->x,(-16000<<14),(-15000<<14));

	ideal->y = target->flip_yco<<4;
	if(flags & CAM_FLGOFFSET)
	{
		offset=ideal->x-(target->flip_xco<<4);
		offset=ABS(offset>>2);
		offset=(offset>>8)*camBasketFactor;
		offset=offset*5/4;

		CamClamp((signed long *)&offset,-5000<<14,5000<<14);

		ideal->y += offset;
		CamClamp(&ideal->y,-12000<<14,12000<<14);
	}

	ideal->z = 6000<<14;

	ideal->thi = LFPAtn(ABS(ideal->x-(target->flip_xco<<4)),
			    ideal->z-ph);
	ideal->thi=0xFA0 - ((0xFA0-ideal->thi)/3);
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateSideline2(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		ph;
	int		offset;

	// get target height
	ph = (target->flip_zco<<4);
	if (!(flags & CAM_FLGTARGETBALL))
		ph += (2000<<14);

	// set values
	ideal->theta = -16384;

	ideal->x = (target->flip_xco<<4)-(10000<<14);
	CamClamp(&ideal->x,(-16000<<14),(-15000<<14));

	ideal->y = target->flip_yco<<4;
	if(flags & CAM_FLGOFFSET)
	{
		offset=ideal->x-(target->flip_xco<<4);
		offset=ABS(offset>>2);
		offset=(offset>>8)*camBasketFactor;
		offset=offset*5/4;

		CamClamp((signed long *)&offset,-5000<<14,5000<<14);

		ideal->y += offset;
		CamClamp(&ideal->y,-12000<<14,12000<<14);
	}

	ideal->z = 12000<<14;

	ideal->thi = LFPAtn(ABS(ideal->x-(target->flip_xco<<4)),
			    ideal->z-ph);
//	ideal->thi=0xFA0 - ((0xFA0-ideal->thi)/3);
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateCircle(camPos *cur,camPos *ideal,playert *target,int flags)
{
	matrix
		m;
	vector
		v,
		v2;
	extern int frame;

	v.x=0;
	v.y=3500;
	v.z=-8000;

	MakeMatPRH(&m,0,0,frame*128);
	VecMulMat(&v,&m,&v2);

	ideal->thi=4096;
	ideal->theta=frame*128;
	ideal->x = v2.x<<14;
	ideal->y = v2.y<<14;
	ideal->z = v2.z<<14;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateOverhead(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		ph;
	int		offset;

	/*
	 * get target
	 */

	// get target height
	if(flags & CAM_FLGTARGETBALL)	ph=(target->flip_zco<<4) + (1000<<14);
	else				ph=2000<<14;

	if(ph<(2000<<14))		ph=2000<<14;

	ideal->theta=0;
	ideal->thi=16384;
	ideal->x=target->flip_xco << 4;
	CamClamp(&ideal->x,-3000<<14,3000<<14);
	ideal->z=(16000<<14)+ph;

	ideal->y = target->flip_yco<<4;
	if(flags & CAM_FLGOFFSET)
	{
		offset=ideal->z-(2000<<14);
		offset=ABS(offset>>2);
		offset=(offset>>8)*camBasketFactor;

		ideal->y += offset;
		CamClamp(&ideal->y,-10000<<14,10000<<14);
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluatePress(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int
		targetX,
		targetY,
		targetZ,
		offset;

	targetX = target->flip_xco;

	// If target moves close to near side line, move camera focus ahead of it...

	offset = targetX - (-4500<<10);
	if (offset < 0)
		targetX += (offset << 1);
	if (targetX < -(7500<<10))
		targetX = -(7500<<10);

	// Keep target close to the near side-line...

	offset = -(4000<<10);
	if (targetX > offset)
		targetX = offset;

	targetX <<= 4;


	targetZ = (target->flip_zco << 4);

	if (flags & CAM_FLGTARGETBALL)
	{
		// If ball goes above basket, bring camera focus lower...

		if (targetZ > BASKET_Z)
			targetZ -= (targetZ - BASKET_Z) >> 1;
	}
	else
		targetZ += (2000<<14);

	if (targetZ < (2000<<14))
		targetZ = (2000<<14);


	ideal->x=-16000<<14;

	targetY = target->flip_yco<<4;
	if(flags & CAM_FLGOFFSET)
	{
		offset=ideal->x-(target->flip_xco<<4);
		offset=ABS(offset>>2);
		offset=(offset>>8)*camBasketFactor;
		offset=offset*5/4;

		CamClamp((signed long *)&offset,-5000<<14,5000<<14);

		targetY += offset;
		CamClamp((signed long *)&targetY,-12000<<14,12000<<14);
	}

	ideal->y=targetY/2;
	ideal->z=7000<<14;
	ideal->thi = LFPAtn( ABS( ideal->x - targetX ), ideal->z - targetZ );
//	ideal->thi=0xFA0-((0xFA0-ideal->thi)/3);
	ideal->theta=16384-LFPAtn(ideal->x,targetY-ideal->y);
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateSky(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		ph;
	int		offset;

	// get target height
	ph=2000<<14;

	ideal->theta=0;
	ideal->x=target->flip_xco<<4;
	CamClamp(&ideal->x,-3000<<14,3000<<14);
	ideal->thi=8000;
	ideal->z=(LFPSin(ideal->thi)*14000/16384)<<14;

	ideal->y = target->flip_yco<<4;
	if(flags & CAM_FLGOFFSET)
	{
		offset=ideal->z;
		offset=ABS(offset>>2);
		offset=(offset>>8)*camBasketFactor;

		ideal->y += offset;
		CamClamp(&ideal->y,-0xB000000,0x9000000);
	}
	ideal->y-=(LFPCos(ideal->thi)*14000/16384)<<14;
	ideal->z+=ph;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateSky2(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		ph;
	int		offset;

	// get target height
	ph=2000<<14;

	ideal->theta=0;
	ideal->x=target->flip_xco<<4;
	CamClamp(&ideal->x,-3000<<14,3000<<14);
	ideal->thi=4000;
	ideal->z=(LFPSin(ideal->thi)*14000/16384)<<14;

	ideal->y = target->flip_yco<<4;
	if(flags & CAM_FLGOFFSET)
	{
		offset=ideal->z;
		offset=ABS(offset>>2);
		offset=(offset>>8)*camBasketFactor;

		ideal->y += offset;
		CamClamp(&ideal->y,-0xB000000,0x9000000);
	}
	ideal->y-=(LFPCos(ideal->thi)*14000/16384)<<14;
	ideal->z+=ph;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateClassic(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		ph;
	int		offset;
	int		targetY;

	// get target height
	if(flags & CAM_FLGTARGETBALL)	ph=target->flip_zco<<4;
	else				ph=(target->flip_zco<<4) + (2000<<14);

	ideal->z=8000<<14;
	ideal->thi=6000;
	ideal->theta=-16384-6000;

	ideal->x=(target->flip_xco<<4)-(10000<<14);
	CamClamp(&ideal->x,-14000<<14,-12000<<14);

	ideal->y = target->flip_yco<<4;
	if(flags & CAM_FLGOFFSET)
	{
		if(SWITCHED_BASKET_Y>0)
		{
			offset=2*(ideal->z + ABS(ideal->x-(target->flip_xco<<4)));
			offset=offset>>2;
		}
		else if(SWITCHED_BASKET_Y<0)
		{
			offset=2*(ideal->z - ABS(ideal->x-(target->flip_xco<<4)));
			offset=offset>>2;
			offset=-offset;
			offset+=3000<<14;
		}
		else
		{
			offset=1500<<14;
		}
		ideal->y += offset;
	}
	CamClamp(&ideal->y,-0x5000000,0x10BD0000);

}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateBehind(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		switchTheta;
	int		a,j;
	int		x,y,z;
	int		tx,ty,tz;

	switchTheta=ideal->theta;

#if 0

	if (SWITCHED_BASKET_Y > 0)
		ideal->theta = (target->flip_xco>>10)*8192/12000;
	else
		ideal->theta = 32768-((target->flip_xco>>10)*8192/12000);

#else

	x = target->flip_xco >> 1;
	y = SWITCHED_BASKET_Y - target->flip_yco;

	ideal->theta = LFPAtn( y, x );

#endif

	ideal->y=target->flip_yco<<4;
	ideal->x=target->flip_xco<<4;
	ideal->z=2000<<14;
//	if(target->flip_zco > (2000<<10)) ideal->z=target->flip_zco<<4;

	if (!camFirstFrame)
	{
		j=CamAngTime(switchTheta,ideal->theta);
		if(j && (flags & CAM_FLGLIMIT))
		{
			switchTheta=CamAngAdjust(switchTheta,ideal->theta,j);
			ideal->theta=switchTheta;
		}
	}
	ideal->thi=1000;

	a=LFPCos(ideal->thi);
	x=((-LFPSin(ideal->theta)*(8000))>>14)*a;
	y=((LFPCos(ideal->theta)*(8000))>>14)*a;
	z=-LFPSin(ideal->thi)*8000;

	tx = ideal->x;
	ty = ideal->y;
	tz = ideal->z;

	ideal->x -= x;
	ideal->y -= y;
	ideal->z -= z;

	{
		long
			src[3],
			dest[3],
			res[3];

		src[0]=tx>>14;
		src[1]=tz>>14;
		src[2]=ty>>14;

		dest[0]=ideal->x>>14;
		dest[1]=ideal->z>>14;
		dest[2]=ideal->y>>14;

		RayProject(src,dest,res);

		ideal->x=res[0]<<14;
		ideal->z=res[1]<<14;
		ideal->y=res[2]<<14;
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateEnd(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		switchTheta;
	int		a,j;
	int		x,y,z;

	switchTheta=ideal->theta;

#if 0

	if(SWITCHED_BASKET_Y>0)	ideal->theta=(target->flip_xco>>10)*8192/24000;
	else			ideal->theta=32768-((target->flip_xco>>10)*8192/24000);

#else

	x = target->flip_xco >> 1;
	y = SWITCHED_BASKET_Y - target->flip_yco;

	ideal->theta = LFPAtn( y, x );

#endif

	ideal->y=target->flip_yco<<4;
	ideal->x=target->flip_xco<<4;
	ideal->z=2500<<14;
//	if(target->flip_zco > (3000<<10)) ideal->z=target->flip_zco<<4;

	j=CamAngTime(switchTheta,ideal->theta);
	if(j && (flags & CAM_FLGLIMIT))
	{
		switchTheta=CamAngAdjust(switchTheta,ideal->theta,j);
		ideal->theta=switchTheta;
	}
	ideal->thi=2000;

	a=LFPCos(ideal->thi);
	x=((-LFPSin(ideal->theta)*(8000))>>14)*a;
	y=((LFPCos(ideal->theta)*(8000))>>14)*a;
	z=-LFPSin(ideal->thi)*8000;

	ideal->x -= x;
	ideal->y -= y;
	ideal->z -= z;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

int	camReplayDist;
int	camReplayThi;
int	camReplayTheta;
int	camReplayHeight;
int	camReplayTargetNo;
int	camReplayMimicMode;
int	camReplayMimicFlag;


void SetupReplayCam()
{
	camReplayDist=8000;
	camReplayThi=2000;
	camReplayTheta=0;
	camReplayHeight=0;
	camReplayTargetNo=-1;
	camReplayMimicMode=0;
	camReplayMimicFlag=0;
}


void CamEvaluateReplay(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		x,y,z;
	int		p,q,r;
	int		a,f;
	camPos		tmp,tmp2;
	int		tz;

	// get target
	if(camReplayTargetNo==-1)	// BHP or BALL
	{
		// just use assigned target
	}
	else if(camReplayTargetNo<10)	// specific player
	{
		target=&player[camReplayTargetNo];
		flags &= ~CAM_FLGTARGETBALL;
	}

	flags &= ~(CAM_FLGOFFSET | CAM_FLGLIMIT);

	// see if we need to update values from another camera mode
	if(camReplayMimicFlag)
	{
		memcpy(&tmp,ideal,sizeof(tmp));
		camModeFunctions[camReplayMimicMode](cur,&tmp2,target,flags);

		// adjust for user zoom
		CamZoom(&tmp2,&tmp,target,camZoomUser,flags);
		camReplayThi=tmp.thi;
		camReplayTheta=tmp.theta;

		// calculate offset based on 8m distance
		camReplayDist=16000;
		a=LFPCos(camReplayThi);
		x=-((-LFPSin(camReplayTheta)*camReplayDist)>>14)*a;
		y=-((LFPCos(camReplayTheta)*camReplayDist)>>14)*a;
		z=LFPSin(camReplayThi)*camReplayDist;
		x>>=14; y>>=14; z>>=14;

		// get ACTUAL offset
		p=tmp.x-(target->flip_xco<<4); p>>=14;
		q=tmp.y-(target->flip_yco<<4); q>>=14;
		r=tmp.z-(target->flip_zco<<4); r>>=14;

		// adjust distance...
		p=fast_sqrt(p*p+q*q);
		x=fast_sqrt(x*x+y*y);
		if(x) camReplayDist=16000*p/x;

		// now calculate height...
		z=(LFPSin(camReplayThi)*camReplayDist)>>14;
		camReplayHeight=(r-z);
		if(!(flags & CAM_FLGTARGETBALL)) camReplayHeight -= 1500;

		camReplayMimicFlag=0;
	}

	// get base position
	ideal->x=target->flip_xco<<4;
	ideal->y=target->flip_yco<<4;

	tz=(target->flip_zco<<4);
	if(!(flags & CAM_FLGTARGETBALL)) tz += (1500<<14);

	// clamp camHeight...
	if(tz+(camReplayHeight<<14) < 200<<14)
	{
		camReplayHeight=200-(tz>>14);
	}
	ideal->z=tz+(camReplayHeight<<14);

	ideal->thi=camReplayThi;
	ideal->theta=camReplayTheta;

	// deal with "over the top"
	if(ideal->thi > 16384)
	{
		ideal->thi=32768-ideal->thi;
		ideal->theta^=32768;
	}

	// calculcate offset
	a=LFPCos(ideal->thi);
	x=((-LFPSin(ideal->theta)*camReplayDist)>>14)*a;
	y=((LFPCos(ideal->theta)*camReplayDist)>>14)*a;
	z=-LFPSin(ideal->thi)*camReplayDist;

	{
		long	src[3],dest[3],res[3];
		int	f;

		if(ideal->x <  -8800<<14) ideal->x= -8800<<14;
		if(ideal->x >   9800<<14) ideal->x=  9800<<14;
		if(ideal->y < -17600<<14) ideal->y=-17600<<14;
		if(ideal->y >  17600<<14) ideal->y= 17600<<14;
		if(ideal->z <    100<<14) ideal->z=   100<<14;
		if(ideal->z >  19900<<14) ideal->z= 19900<<14;

		src[0]=ideal->x>>14;
		src[1]=ideal->z>>14;
		src[2]=ideal->y>>14;
		dest[0]=(ideal->x-x)>>14;
		dest[1]=(ideal->z-z)>>14;
		dest[2]=(ideal->y-y)>>14;
		f=RayProject(src,dest,res);
		ideal->x=res[0]<<14;
		ideal->z=res[1]<<14;
		ideal->y=res[2]<<14;

		camReplayDist=(int)((float)camReplayDist*(float)f/16384.0);
		CamClamp((signed long *)&camReplayDist,1500,15000);
	}

	// apply
//	ideal->x -= x;
//	ideal->y -= y;
//	ideal->z -= z;

	// apply bounding limits
//	CamClamp(&ideal->z,50<<14,20000<<14);
//	CamClamp(&ideal->x,-9000<<14,9000<<14);
//	CamClamp(&ideal->y,-18000<<14,18000<<14);
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamInitReplay(void)
{
	camReplayTargetNo=-1;
	camReplayMimicFlag=1;
	camReplayMimicMode=camMode;
}



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

extern bool
	ShowReplayPanel;


u32
	ReplayButtons = 0;


void CamProcessReplay(unsigned long p)
{
	u32
		newp;

	int	nb;

	// get new buttons
	newp = p & ~ReplayButtons;
	ReplayButtons = p;
	nb=PracticeMode? 5 : 10;

	// deal with motion

	if(p & CTRL_C_LEFT)		camReplayTheta+=(256*2);
	if(p & CTRL_C_RIGHT)		camReplayTheta-=(256*2);
	if(p & CTRL_C_UP)		camReplayThi-=(256*2);
	if(p & CTRL_C_DOWN)		camReplayThi+=(256*2);
	CamClamp((signed long *)&camReplayThi,0,32768);

	if(p & CTRL_PAD_UP)		camReplayDist=camReplayDist*95/100;
	if(p & CTRL_PAD_DOWN)		camReplayDist=camReplayDist*105/100;
	CamClamp((signed long *)&camReplayDist,1500,15000);

	if(p & CTRL_PAD_LEFT)		camReplayHeight+=50;
	if(p & CTRL_PAD_RIGHT)		camReplayHeight-=50;
	CamClamp((signed long *)&camReplayHeight,0,4000);

	// advance player...
	if(newp & CTRL_FIRE_A) camReplayTargetNo--;
	if(newp & CTRL_FIRE_B) camReplayTargetNo++;
	if(camReplayTargetNo<-1) camReplayTargetNo=nb-1;
	if(camReplayTargetNo>9) camReplayTargetNo=-1;

	// advance mode...
	if(newp & CTRL_SHOULDER_L)
	{
		camReplayMimicMode=(camReplayMimicMode+1)%CAM_NUMUSERMODES;
		camReplayMimicFlag=1;
	}

	if(newp & CTRL_SHOULDER_R)
		ShowReplayPanel = !ShowReplayPanel;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

extern s32
	FTwait,
	FTstage,
	FTheight;


static s32
	FTangle;


void CamEvaluateSpecial(camPos *cur,camPos *ideal,playert *target,int flags)
{
	int		x,y,z,i,f,thi,theta;
	int		p,q,r;
	int		ph,pz;
	static s32
		tween;

	ph=0;

	// check for circle mode...
	if(camSpecialMode==CAM_SPECIAL_CIRCLE)
	{
		CamEvaluateCircle(cur,ideal,target,flags);
		return;
	}

	// grab overriding player target...
	// don't reset target yet, in case it's useful first time round...
	// eg. for setting Xflip on fixed angle static cams...
	if(camSpecialTargetNo>=0)	target=&player[camSpecialTargetNo];

	f=specialDefs[camSpecialMode].flags;

	// first time through?
	if (camSpecialFirstTime || AttractMode)
	{
		if (camSpecialMode == CAM_SPECIAL_FREETHROW)
			specialDefs[camSpecialMode].z = FTheight;

		if (camSpecialMode == CAM_SPECIAL_BASTARD)
			specialDefs[camSpecialMode].z = target->nodeVector[NODE_HEAD].y - 1900;

		tween = 0;

		camSpecialXFlip=0;
		camSpecialRotate=0;

//		if((SWITCHED_BASKET_Y < 0) &&
		if((camActionBasket < 0) &&
		    !(specialDefs[camSpecialMode].flags & SPECIAL_NOYFLIP))
		{
			camSpecialRotate=1;
		}

		if((f & SPECIAL_XFLIP) && target)
		{
			x=target->flip_xco;
			if(camSpecialRotate)	x=-x;
			if(x < 0)		camSpecialXFlip=1;
		}
	}

	// get target
	if(camSpecialTargetNo==-2)	target=NULL;		// static

	// get pos
	x=specialDefs[camSpecialMode].x << 14;
	y=specialDefs[camSpecialMode].y << 14;
	z=specialDefs[camSpecialMode].z << 14;

	if(camSpecialXFlip) x=-x;

	// get target pos
	if(target)
	{
		p=target->flip_xco<<4;
		q=target->flip_yco<<4;
		r=target->flip_zco<<4;
		if(camSpecialRotate) { p=-p; q=-q; }

		if(target!=&ball)
		{
			r+=1750<<14;
		}

		// add on player pos if tracking cam
		if(!(f & SPECIAL_STATIC))
		{
			x+=p;
			y+=q;
			z+=r;
		}
	}

	// now, get angles... depending on targeting or fixed cam...
	if(!target)
	{
		thi=specialDefs[camSpecialMode].thi;
		theta=specialDefs[camSpecialMode].theta;
		if(camSpecialXFlip) theta=-theta;
	}
	else
	{
		// need to get vector to target...
		theta=LFPAtn((p)-x,(q)-y);

		i=fast_sqrt( ((p-x)>>14)*((p-x)>>14) +
			     ((q-y)>>14)*((q-y)>>14) );
		i<<=14;

		pz=(r);
//		if(pz<(1750<<14)) pz=1750<<14;
		thi=LFPAtn(i,z-(pz+ph));
		theta=theta-16384;
	}

	if (camSpecialMode == CAM_SPECIAL_BASTARD)
		thi = specialDefs[camSpecialMode].thi;

	if(camSpecialRotate)
	{
		x=-x;
		y=-y;
		theta^=32768;
	}

	if (camSpecialMode == CAM_SPECIAL_FREETHROW_CPU)
	{
		matrix
			mtx;
		vector16
			vec;

		if (camSpecialFirstTime)
		{
			tween = 0;
		}

		x = ((camActionBasket * -112) * tween) >> 5;
		y = camActionBasket * ((((3670-2500) * tween) >> 5) + 2500);
		z = (((852*4 - FTheight) * tween) >> 5) + FTheight;

		x <<= (14+2);
		y <<= (14+2);
		z <<= (14+0);

		theta = (((33280 - 32768) * tween) >> 5) + 32768;
		if (camActionBasket < 0)
			theta ^= 32768;

		thi = (((1536*2) * tween) >> 5) - 1536;

		if (FTstage)
		{
			if (tween < 32)
				tween++;
		}
	}

	// update vals
	ideal->x=x;
	ideal->y=y;
	ideal->z=z;
	ideal->theta=theta;
	ideal->thi=thi;

	// snap on first time..
	if(camSpecialFirstTime)
	{
		if(camSpecialCut)
		{
			CamQueueSnap(ideal);
			memcpy(&camCurrent,ideal,sizeof(camCurrent));
		}
		camSpecialFirstTime=0;
	}


	if (camSpecialMode == CAM_SPECIAL_FREETHROW)
	{
		if (FTwait)
			FTwait--;

		if (!FTwait && !ShowFreeThrowHelp)
		{
			if (AwayFreeThrow)
				Crowd_FreeThrow( TRUE );

			camSpecialMode = CAM_SPECIAL_FREETHROW2;
			camSpecialFirstTime = TRUE;
			camSpecialCut = TRUE;
		}
	}
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

long	planePos[]=
{
	990,95,0,			// xmax
	-890,95,0,			// xmin
	0,2000,0,			// ymax
	0,0,0,				// ymin
	0,95,1770,			// zmax
	0,95,-1770,			// zmin
};

long	planeVec[]=
{
	-(714-95),(1527-990),0,		// xmax
	(714-95),(1527-990),0,		// xmin
	0,-100,0,			// ymax
	0,100,0,			// ymin
	0,(2267-1770),-(714-95),	// zmax
	0,(2267-1770),(714-95),		// zmin
};

int	preNorm=0;

int RayProject(long *src,long *dest,long *res)
{
	int	i,j,k;
	long	*pp,*pn;
	int	x,y,z;
	int	at,ab;
	float	aa,ra;

	// normalise vectors
	if(!preNorm)
	{
		for(i=0;i<6;i++)
		{
			planePos[i*3+0]*=10;
			planePos[i*3+1]*=10;
			planePos[i*3+2]*=10;

			j=planeVec[i*3+0]*planeVec[i*3+0]+
			  planeVec[i*3+1]*planeVec[i*3+1]+
			  planeVec[i*3+2]*planeVec[i*3+2];
			j=fast_sqrt(j);
			planeVec[i*3+0]=planeVec[i*3+0]*16384/j;
			planeVec[i*3+1]=planeVec[i*3+1]*16384/j;
			planeVec[i*3+2]=planeVec[i*3+2]*16384/j;
		}
		preNorm=1;
	}

	// see if the source is inside...
	ra=1.0;
	for(i=0;i<6;i++)
	{
		pp=&planePos[i*3];
		pn=&planeVec[i*3];

		x=src[0] - pp[0];
		y=src[1] - pp[1];
		z=src[2] - pp[2];

		j=(x*pn[0])+(y*pn[1])+(z*pn[2]);
		if(j<0) return(-1);

		// get alpha
		at=0; ab=0;
		for(j=0;j<3;j++)
		{
			at+=(pp[j]-src[j])*pn[j];
			ab+=(dest[j]-src[j])*pn[j];
		}

		if((at<0 && ab>=0) || (at>0 && ab<=0)) continue;
		if(!ab) continue;
		if(at<0) { at=-at; ab=-ab; }

		if(at>ab) continue;

		// collision..
		aa=(float)at/(float)ab;
		if(aa<ra) ra=aa;
	}

	// get actual pos...
	for(i=0;i<3;i++)
	{
		res[i]=(long)((float)src[i] + (((float)dest[i]-(float)src[i])*ra));
	}

	return((int)(ra*16384.0));
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluate3PtContest(camPos *cur,camPos *ideal,playert *target,int flags)
{
	s32
		x,
		y;

	x = -target->flip_xco;
	y = -target->flip_yco;

	if (y > 0)
		y += -BASKET_Y;
	else
		y += +BASKET_Y;

	x >>= 10;
	x *= 2048;

	y >>= 10;
	y *= 2048;

	ideal->x = -x<<4;
	ideal->y = (BASKET_Y - y)<<4;
	ideal->z = 3584<<14;

	ideal->thi = 1920;
	ideal->theta = LFPAtn( y, -x );
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateFaceKB(camPos *cur,camPos *ideal,playert *target,int flags)
{
	s32
		x,
		y,
		z;

	x = (target->flip_xco << 4);
	y = (target->flip_yco << 4);
	z = (2000 << 14);

	ideal->x = x - (2000 << 14);
	ideal->y = y + (4000 << 14);
	ideal->z = z;

	ideal->theta = -26000;
	ideal->thi = 0;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateFaceSO(camPos *cur,camPos *ideal,playert *target,int flags)
{
	s32
		x,
		y,
		z;

	x = (target->flip_xco << 4);
	y = (target->flip_yco << 4);
	z = (s32)target->nodeVector[NODE_HEAD].y << 14;

	ideal->x = x - (4000 << 14);
	ideal->y = y - (1000 << 14);
	ideal->z = z + (1000 << 14);

	ideal->theta = -14000;
	ideal->thi = 0;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateFaceSP(camPos *cur,camPos *ideal,playert *target,int flags)
{
	s32
		x,
		y,
		z;

	x = (target->flip_xco << 4);
	y = (target->flip_yco << 4);
	z = (s32)target->nodeVector[NODE_HEAD].y << 14;

	ideal->x = x + (4000 << 14);
	ideal->y = y - (4000 << 14);
	ideal->z = z + (4000 << 14);

	ideal->theta = 8192;
	ideal->thi = 6000;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateDunkAI(camPos *cur,camPos *ideal,playert *target,int flags)
{
	s32
		x,
		y,
		z;

	x = (target->flip_xco << 4);
	y = (target->flip_yco << 4);
	z = (target->flip_zco << 4);

	ideal->x = x + (4000 << 14);
	ideal->y = y;
	ideal->z = z + (4000 << 14);

	ideal->theta = 16384;
	ideal->thi = 6000;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamEvaluateScore(camPos *cur)
{
	matrix
		mtx;
	vector16
		vec;
	s32
		n,
		dn;

	cur->theta += 128;

	MakeMatPRH( &mtx, 0, 0, cur->theta );

	vec.x = 0;
	vec.y = 0;
	vec.z = -16000;

	Vec16MulMat( &vec, &mtx, &vec );

	n = (cur->x >> 14);
	if (abs( vec.x - n ) < 256)
		n = vec.x;
	else if (n < vec.x)
		n += 256;
	else
		n -= 256;
	cur->x = (n << 14);

vec.y = vec.z;
	n = (cur->y >> 14);
	if (abs( vec.y - n ) < 256)
		n = vec.y;
	else if (n < vec.y)
		n += 256;
	else
		n -= 256;
	cur->y = (n << 14);


	n = (cur->z >> 14);
	if (abs( 16000 - n ) < 64*2)
		n = 16000;
	else if (n < 16000)
		n += 64*2;
	else
		n -= 64*2;
	cur->z = (n << 14);


	n = MANHATTAN( cur->y>>14, cur->x>>14 );
	n = LFPAtn( n, (cur->z >> 14) - 16000 );

	if (abs( cur->thi - n ) < 128)
		cur->thi = n;
	else if (cur->thi < n)
		cur->thi += 128;
	else
		cur->thi -= 128;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

