/***************************************************************************
 *
 * camera.c
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

/*
 * includes
 */

#include "ult_64.h"
#include "ctrl.h"
#include "lfptypes.h"
#include "define.h"
#include "ml.h"
#include "camera.h"
#include "menu.h"
#include "cammodes.h"
#include "ft.h"
#include "scorebrd.h"
#include "misc.h"


extern s8
	AttractBasket;


int manualReplayFlag = 0;


/*
 * defines
 */

#define ABS(x)			(((x)<0)?(-(x)):(x))

#define CAM_QUEUESIZE		8

#define CAM_MAXPATHVEL		0x800000
#define CAM_MAXANGVEL		1024

/*
 * typedefs
 */

/*
 * structures
 */


/*
 * globals
 */

int	lastMultiCam=-1;

// global camera variables, for exporting to render.c, etc...
global SI	si___gCamThi,si___gCamTheta;
global SI	si___gCamX,si___gCamY,si___gCamZ;

int		camBasketFactor=0;
int		camSpecialLimit=0;
int		camFirstFrame=0;
int		camFirstQuarter=0;

// camera pipeline stages
camPos		camIdeal;			// where the control function wants to be
camPos		camCurrent;			// where we ARE - read-only for functions
camPos		camQueue[CAM_QUEUESIZE];	// queue of positions for damping
camPos		camQueueSum;			// sum of queue
camPos		camQueueAvg;			// average of queue
camPos		camZoomed;			// un-zoomed current pos
int		camQueuePtr;
int		camLetterBox;			// letterbox?

// publicly changeable values
int		camMode=0;
int		camZoomUser=96;//64;
int		camTargetNo=-1;
int		camDoSwitch=0;

int		camMoving;

int		inGameSpecial=0,inGameTimeout=0;
int		specialZoom=0,specialZoomDir=0;


s32
	ScoreBoardCam = FALSE,
	ScoreBoardTime = 0;


/********************************************************************/

typedef struct SCam
{
	s16
		x,
		y,
		z,
		yaw,
		pitch;
} SCam;


static u8
	*TipOffPath = NULL;

static SCam
	*TipOffCam;

static s32
	TipOffTimer = 0;


void Cam_TipOff_Init()
{
	if (!TipOffPath)
		TipOffPath = AllocAndLoadFile( "TipOff.CAM", MEM_TMP );

	TipOffTimer = *(s32 *)&TipOffPath[4*4] - 1;
	TipOffCam = (SCam *)&TipOffPath[5*4];
}


void Cam_TipOff_Deinit()
{
	if (TipOffPath)
	{
		camFirstFrame = TRUE;

		MemFree( TipOffPath );
		TipOffPath = NULL;
	}

	TipOffTimer = 0;
}


s32 Cam_TipOff_Active()
{
	return( TipOffTimer );
}


static void Cam_TipOff_Update( camPos *cur )
{
	SCam
		*cam;

	cam = TipOffCam;

	cur->x = (int)cam->x << 14;
	cur->y = (int)cam->z << 14;
	cur->z = (int)cam->y << 14;

	cur->theta = cam->yaw;
	cur->thi = cam->pitch;
	cam++;

	TipOffCam = cam;

	if (TipOffTimer)
		TipOffTimer--;

	if (!TipOffTimer)
	{
		Cam_TipOff_Deinit();
		camFirstFrame = FALSE;
	}
}


/********************************************************************/

// wrappers for mike's in-game calls
void		CameraSpecialStart(int zoom)
{
	int	mode,targ;

	if(inGameSpecial) return;

	if(PracticeMode) return;	// bug # 925 (Sort of)

	if (zoom)
		mode = 2;
	else
		mode = random( 2 );

	targ = random( 2 ) ? -1 : -2;

	switch(mode)
	{
		case 0:
			mode=CAM_SPECIAL_BASELINE2;
			lastMultiCam=mode;
			CamStartSpecial(mode,targ,1,0);
			break;
		case 1:
			mode=CAM_SPECIAL_OVERHEAD2;
			lastMultiCam=mode;
			CamStartSpecial(mode,targ,1,0);
			break;
		case 2:
			specialZoom=1;
			specialZoomDir=8;
			break;
	}

	inGameSpecial=1;
//	inGameTimeout=30*4;	// emergency timeout
	inGameTimeout=30*3;	//- emergency timeout Bug #931
}

void		CameraSpecialEnd(void)
{
	inGameTimeout=30;
}

void		CameraSpecialEndQuick(void)
{
	inGameTimeout=1;
}

/*
 * function prototypes
 */

void		CamClamp(signed long *result,signed long min,signed long max);
void		CamZoom(camPos *cur,camPos *dest,playert *target,int zoom,int flags);

/*
 * code
 */

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

int MyRnd(void)
{
	return( rand() & 0xFF );
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamQueueInit(void)
{
	memset(camQueue,0,sizeof(camQueue));
	memset(&camQueueSum,0,sizeof(camQueueSum));
	camQueuePtr=0;
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamQueueAdd(camPos *p)
{
	camPos		*ci;
	signed short	diff;
	int		i;

	// get entry to be modified
	ci=&camQueue[camQueuePtr];

	// adjust position
	camQueueSum.x += (p->x >> 4) - (ci->x >> 4);
	camQueueSum.y += (p->y >> 4) - (ci->y >> 4);
	camQueueSum.z += (p->z >> 4) - (ci->z >> 4);

	// update queue entry
	*ci=*p;

	// build angles relative to new thi/theta, to avoid wraparound
	camQueueSum.thi=0;
	camQueueSum.theta=0;

	for(i=0;i<CAM_QUEUESIZE;i++)
	{
		diff=(signed short)(camQueue[i].thi - ci->thi);
		camQueueSum.thi+=(signed)(diff + ci->thi);
		diff=(signed short)(camQueue[i].theta - ci->theta);
		camQueueSum.theta+=(signed)(diff + ci->theta);
	}

	// get averages... queue size is 8, shift of 4 to avoid overflow
	// result - just multiply by 2 to get avg...
	camQueueAvg.x = camQueueSum.x * 2;
	camQueueAvg.y = camQueueSum.y * 2;
	camQueueAvg.z = camQueueSum.z * 2;
	camQueueAvg.thi = camQueueSum.thi / CAM_QUEUESIZE;
	camQueueAvg.theta = camQueueSum.theta / CAM_QUEUESIZE;

	// update ptr
	camQueuePtr=(camQueuePtr+1)%CAM_QUEUESIZE;
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

int CamPosTime(signed long cur,signed long dest)
{
	int		d;

	d=(dest-cur); d=ABS(d);
	return(d/CAM_MAXPATHVEL);
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

int CamAngTime(signed long cur,signed long dest)
{
	signed short	d;

	d=(dest-cur); d=ABS(d);
	return(d/CAM_MAXANGVEL);
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

signed long CamPosAdjust(signed long cur,signed long ideal,int time)
{
	int		d;

	d=ideal-cur;
	if(time) d/=time;
	d+=cur;
	return(d);
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

signed short CamAngAdjust(signed short cur,signed short ideal,int time)
{
	signed short	d;

	d=ideal-cur;
	if(time) d/=time;
	d+=cur;
	return(d);
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamPathLimit(camPos *cur,camPos *ideal,camPos *actual)
{
	int	i,j,k;

	// get maximum time...
	j=1;
	k=CamPosTime(cur->x,ideal->x);		if(k>j) j=k;
	k=CamPosTime(cur->y,ideal->y);		if(k>j) j=k;
	k=CamPosTime(cur->z,ideal->z);		if(k>j) j=k;
	k=CamAngTime(cur->thi,ideal->thi);	if(k>j) j=k;
	k=CamAngTime(cur->theta,ideal->theta);	if(k>j) j=k;

	if(j>5)		camMoving=1;
	else		camMoving=0;

	// evaluate new, speed limited pos
	actual->x=CamPosAdjust(cur->x,ideal->x,j);
	actual->y=CamPosAdjust(cur->y,ideal->y,j);
	actual->z=CamPosAdjust(cur->z,ideal->z,j);
	actual->thi=CamPosAdjust(cur->thi,ideal->thi,j);
	actual->theta=CamPosAdjust(cur->theta,ideal->theta,j);
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamQueueSnap(camPos *p)
{
	int		i;

	// fill queue
	for(i=0;i<CAM_QUEUESIZE;i++)
	{
		memcpy(&camQueue[i],p,sizeof(camPos));
	}

	// update sum
	camQueueSum.x=(p->x>>4) * CAM_QUEUESIZE;
	camQueueSum.y=(p->y>>4) * CAM_QUEUESIZE;
	camQueueSum.z=(p->z>>4) * CAM_QUEUESIZE;
	camQueueSum.thi=p->thi * CAM_QUEUESIZE;
	camQueueSum.theta=p->theta * CAM_QUEUESIZE;

	// reset ptr
	camQueuePtr=0;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamClamp(signed long *result,signed long min,signed long max)
{
	if(*result > max) *result=max;
	if(*result < min) *result=min;
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 * called at start of each quarter!
 *
 ***************************************************************************/

void init_camera(void)
{
	inFreeThrow=0;

	camTargetNo=-1;
	camSpecialMode=-1;
	inGameSpecial=0;
	inGameTimeout=0;
	specialZoom=0;
	specialZoomDir=0;
	camLetterBox=0;
	camBasketFactor=0;
	if(camFirstQuarter)
	{
		camFirstFrame=1;
		camFirstQuarter=0;
		SetupReplayCam();
	}
}


void CamInitGame(void)
{
	CamQueueInit();
	memset(&camIdeal,0,sizeof(camIdeal));
	memset(&camCurrent,0,sizeof(camCurrent));
	camFirstQuarter=1;
#ifdef JAPAN
	camZoomUser=pcl__gPreferences->ub___ZoomSetting;
#else
	camZoomUser=96;//64;
#endif

	if (C3PT || PracticeMode || AttractMode)
		TipOffTimer = 0;
	else
		Cam_TipOff_Init();

	ScoreBoardCam = FALSE;
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void move_camera(void)
{
	playert		*target;
	int		flags,z;
	int		tg;
	extern int	shellDelay;

#if 0
	// ensure no in-game special cams when not required...
	if(inGameSpecial && pcl__gPreferences->sb___SpecialCameras!=1)
	{
		CameraSpecialEnd();
		inGameTimeout=1;
	}
#endif

	// adjust "special" zoom
	specialZoom += specialZoomDir;
	if(specialZoom > 255) specialZoom=255;
	if(specialZoom < 0)
	{
		specialZoom=0;
		specialZoomDir=0;
	}

	// check ingame special camera timeout
	if(inGameSpecial && inGameTimeout)
	{
		inGameTimeout--;
		if(!inGameTimeout)
		{
			if(specialZoom || specialZoomDir)
			{
				specialZoomDir=-16;
			}

			if(camSpecialMode!=-1)
			{
				CamStopSpecial();
				specialZoom=specialZoomDir=0;
			}

			inGameSpecial=0;
		}
	}

	// ensure the player coords are valid
	FlipPlayerCoords();

	// ensure the camera switches ends a little more gently
	if(tip_off_flag) tg=0;
	else if(in_play_flag) tg=(SWITCHED_BASKET_Y>0)?256:-256;
	else tg=(SWITCHED_BASKET_Y>0)?128:-128;

	if(camBasketFactor>=tg+16) camBasketFactor-=16;
	else if(camBasketFactor>tg) camBasketFactor=tg;
	else if(camBasketFactor<=tg-16) camBasketFactor+=16;
	else if(camBasketFactor<tg) camBasketFactor=tg;

	// choose a target
	flags=0;
	if(camTargetNo==-1)
	{
		if (camSpecialMode >= 0)
		{
			if (last_dunker >= 0)
				target = &player[last_dunker];
			else
			{
				if (possession_flag)
					target = bhp;
				else
				{
					target = &ball;
					flags |= CAM_FLGTARGETBALL;
				}
			}
		}
		else
		{
			if (free_throws)
				target = &player[free_thrower];
			else
			{
				if (possession_flag)
					target=bhp;
				else
				{
					target=&ball;
					flags |= CAM_FLGTARGETBALL;
				}
			}
		}
	}
	else
	{
		target=&player[camTargetNo];
	}


	if (camMode == CAM_MODE_3PT_CONTEST)
	{
		target = bhp;
		flags &= ~CAM_FLGTARGETBALL;
	}


	if(!tip_off_flag)	flags |= CAM_FLGOFFSET;
	flags |= CAM_FLGLIMIT;		// for END and BEHIND


	if (AttractMode)
	{
		target = &player[camTargetNo];
		camSpecialTargetNo = camTargetNo;

		if (camTargetNo == 10)
			flags |= CAM_FLGTARGETBALL;

		flags &= ~CAM_FLGOFFSET;

		if (AttractBasket >= 0)
		{
			current_basket_y = +BASKET_Y;
			camActionBasket = +1;
		}
		else
		{
			current_basket_y = -BASKET_Y;
			camActionBasket = -1;
		}
	}


	// call current camera mode evaluator
	if (ScoreBoardCam)
	{
		ScoreBoardTime++;
		CamEvaluateScore( &camCurrent );
		camCurrent.theta = (s16)camCurrent.theta;
		CamQueueAdd( &camCurrent );
	}
	else if (Cam_TipOff_Active())
	{
		Cam_TipOff_Update( &camCurrent );
		camCurrent.theta = (s16)camCurrent.theta;
//		if (camFirstFrame)
		{
			CamQueueSnap(&camCurrent);
//			camFirstFrame = FALSE;
		}
//		CamQueueAdd( &camCurrent );
	}
	else if((!manualReplayFlag) && (camSpecialMode==-1))
	{
//		if (tip_off_flag)
//			camModeFunctions[CAM_MODE_PRESS](&camCurrent,&camIdeal,target,flags);
//		else
			camModeFunctions[camMode](&camCurrent,&camIdeal,target,flags);

		camIdeal.theta=(signed short)(camIdeal.theta);

		z=camZoomUser+((160-camZoomUser)*specialZoom/256);
		if(z<camZoomUser) z=camZoomUser;

//		if(!tip_off_flag)
			CamZoom(&camIdeal,&camZoomed,target,z,flags);
//		else
//			camZoomed=camIdeal;

		if(camFirstFrame)
		{
			CamQueueSnap(&camZoomed);
			camCurrent=camZoomed;
			camFirstFrame=0;
		}

		CamQueueAdd(&camZoomed);
		CamPathLimit(&camCurrent,&camQueueAvg,&camCurrent);
	}
	else if(manualReplayFlag)
	{
		camModeFunctions[CAM_MODE_REPLAY](&camCurrent,&camIdeal,target,flags);
		camIdeal.theta=(signed short)(camIdeal.theta);
		CamQueueAdd(&camIdeal);
		camCurrent=camQueueAvg;
//		CamPathLimit(&camCurrent,&camQueueAvg,&camCurrent);
	}
	else if(camSpecialMode!=-1)
	{
		camModeFunctions[CAM_MODE_SPECIAL](&camCurrent,&camIdeal,target,flags);
		camIdeal.theta=(signed short)(camIdeal.theta);
		CamQueueAdd(&camIdeal);
		if(camSpecialLimit)
		{
			CamPathLimit(&camCurrent,&camQueueAvg,&camCurrent);
		}
		else
		{
			camCurrent=camQueueAvg;
		}
	}

	// export values
	si___gCamX=camCurrent.x;
	si___gCamY=camCurrent.y;
	si___gCamZ=camCurrent.z;
	si___gCamThi=camCurrent.thi;
	si___gCamTheta=camCurrent.theta;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamZoom(camPos *cur,camPos *dest,playert *target,int zoom,int flags)
{
	int		x,y,z;

	// get target pos
	x=target->flip_xco<<4;
	y=target->flip_yco<<4;
	z=target->flip_zco<<4;

	if(!(flags & CAM_FLGTARGETBALL))	z+=1500<<14;

	// get delta
	x-=cur->x;
	y-=cur->y;
	z-=cur->z;

	// adjust
	x>>=8; x=x*zoom/256; x<<=8;
	y>>=8; y=y*zoom/256; y<<=8;
	z>>=8; z=z*zoom/256; z<<=8;

	// copy thi/theta
	dest->thi=cur->thi;
	dest->theta=cur->theta;

	// write back
	dest->x = cur->x + x;
	dest->y = cur->y + y;
	dest->z = cur->z + z;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamNormal(void)
{
	camTargetNo=-1;
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamStartSpecial(int mode,int targetNo,int cutFlag,int limit)
{
	inGameSpecial=0;
	inGameTimeout=0;
	specialZoom=specialZoomDir=0;

	FlipPlayerCoords();

	camSpecialMode=mode;
	camSpecialTargetNo=targetNo;
	camSpecialFirstTime=1;
	camSpecialCut=cutFlag;
	camSpecialLimit=limit;
	if(cutFlag) camSpecialCutOnReturn=1;

	// grab action basket before replay starts...
	if(!replay_flag)
	{
		if(scored_flag!=0)
		{
			camActionBasket=SWITCHED_SCORED_FLAG;
		}
		else
		{
			// hmmm... could be in-game... look at bhp
			if(bhp)
			{
				if(bhp->flip_yco < 0)	camActionBasket=-1;
				else			camActionBasket=1;
			}
			else
			{
				if(ball.flip_yco<0)	camActionBasket=-1;
				else			camActionBasket=1;
			}
		}
	}

	ScoreBrd_Update();
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamStopSpecial(void)
{
	specialZoom=specialZoomDir=0;

	camSpecialMode=-1;
	inGameSpecial=0;
	if(camSpecialCutOnReturn)
	{
		move_camera();
		CamQueueSnap(&camZoomed);
		camCurrent=camZoomed;
		move_camera();		// ensure it gets exported...
		camSpecialCutOnReturn=0;
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamStartActionReplay(int multi)
{
	int		cam,i;
	playert		*scorePlayer;
s32 x,y,hack=FALSE;

	if (PracticeMode)
		return;

	// ensure the player coords are valid
	FlipPlayerCoords();

	// letter-box screen for action replays
	camLetterBox=1;

	// decide now whether or not to do a switch mid-way (not in multi-replays)
	if (multi)
		camDoSwitch=0;
	else
	{
		camDoSwitch = random( 8 );
		camDoSwitch = !camDoSwitch;
	}

	// set "IN-YO-FACE-MUTHA!" coordinates... if we have a ball handler
	if(last_handler>=0 && last_handler<10)
	{
		scorePlayer=&player[last_handler];
		camActionX=scorePlayer->flip_xco<<4;
		camActionY=scorePlayer->flip_yco<<4;
		camActionAngle=(signed short)(scorePlayer->flip_angle);
		camActionPlayer=scorePlayer-&player[0];

		// ensure we don't get 180 degree wierdness...
		i=0;
//		if(camActionAngle>8192 && camActionAngle<32768-8192 && SWITCHED_BASKET_Y > 0) i=1;
//		if(camActionAngle<-8192 && camActionAngle>-(32768-8192) && SWITCHED_BASKET_Y < 0) i=1;
		if(camActionAngle>8192 && camActionAngle<32768-8192 && SWITCHED_SCORED_FLAG > 0) i=1;
		if(camActionAngle<-8192 && camActionAngle>-(32768-8192) && SWITCHED_SCORED_FLAG < 0) i=1;
		if(!i) camDoSwitch=0;

#if 0
// Temp - only use IN-YO-FACE camera...
hack = TRUE;
x=LFPCos(camActionAngle)*1750;
y=LFPSin(camActionAngle)*1750;
specialDefs[CAM_SPECIAL_INYOURFACE].x=(camActionX + x)>>14;
specialDefs[CAM_SPECIAL_INYOURFACE].y=(camActionY + y)>>14;
specialDefs[CAM_SPECIAL_INYOURFACE].z=1500*player[camActionPlayer].scale/0xE8;
#endif
	}
	else
	{
		camActionX=0;
		camActionY=0;
		camActionPlayer=-1;
	}

	// if switching, use tracking cam for first phase
	if(camDoSwitch)
	{
		cam = random( 3 );
		CamStartSpecial(CAM_SPECIAL_SHOULDER1+cam,-1,1,0);
	}
	else
	{
		if(multi)
		{
			do
			{
				cam = random( 9 );
				cam+=CAM_SPECIAL_BACKBOARD;

				// remove undesirable (ie crap) modes
				if(cam==CAM_SPECIAL_OVERHEAD2) cam=CAM_SPECIAL_SIDEHOOP;
				if(cam==CAM_SPECIAL_BASELINE2) cam=CAM_SPECIAL_CHEER;

			} while(cam==lastMultiCam);
			lastMultiCam=cam;
			i = (multi==2)?-1:-2;
			if (cam == CAM_SPECIAL_BACKBOARD ||
					cam == CAM_SPECIAL_BASKET)
				i = -1;
			CamStartSpecial(cam,i,1,0);
		}
		else
		{
			// 20% chance of basket camera...
			cam = random( 5 );
			if (!cam)
				cam = CAM_SPECIAL_BASKET;
			else
			{
				// use angular following cam for non-switchy things
				// 'cos a completely fixed cam is a bit dull...
				cam = random( 9 );
				cam += CAM_SPECIAL_BACKBOARD;
			}
#if 0
if (hack)
	CamStartSpecial(CAM_SPECIAL_INYOURFACE,camActionPlayer,1,0);			// Temp - only use IN-YO-FACE camera.
else
#endif
			CamStartSpecial(cam,-1,1,0);
		}
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamStopActionReplay(void)
{
	CamStopSpecial();
	camLetterBox=0;
	camActionX=0;
	camActionY=0;
	move_camera();
}



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamSwitchActionReplay(void)
{
	int		x,y,z;
	int		cam;

	// ensure the player coords are valid
	FlipPlayerCoords();

	// maybe we don't wanna switch...
	if(!camDoSwitch) return;

	// if we got a ball handler, 50% chance of IN-YO-FACE! cam
	if(camActionX && camActionY && random( 2 ))
	{
		// update CAM_SPECIAL_INYOURFACE coordinates...
		x=LFPCos(camActionAngle)*1750;
		y=LFPSin(camActionAngle)*1750;
		specialDefs[CAM_SPECIAL_INYOURFACE].x=(camActionX + x)>>14;
		specialDefs[CAM_SPECIAL_INYOURFACE].y=(camActionY + y)>>14;
		specialDefs[CAM_SPECIAL_INYOURFACE].z=1500*player[camActionPlayer].scale/0xE8;

		// cut to it
		CamStartSpecial(CAM_SPECIAL_INYOURFACE,camActionPlayer,1,0);
	}
	else
	{
		// just choose a random static camera
		cam = random( 7 );
		cam+=CAM_SPECIAL_BACKBOARD;

		// 50% chance of static/angular following...
		if(camActionPlayer)
			x = camActionPlayer;
		else
			x = random( 2 ) ? -1 : -2;

		CamStartSpecial(cam,x,1,0);
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CamScoreBoard( s32 start )
{
	ScoreBoardCam = start;
	ScoreBoardTime = 0;

	if (start)
		ScoreBrd_Update();
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void Cam_Save( SCamera *camera )
{
	camera->Yaw = si___gCamTheta;
	camera->Pitch = si___gCamThi;

	camera->x = si___gCamX >> 14;
	camera->y = si___gCamZ >> 14;
	camera->z = si___gCamY >> 14;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void Cam_Load( SCamera *camera )
{
	si___gCamTheta = camera->Yaw;
	si___gCamThi = camera->Pitch;

	si___gCamX = camera->x << 14;
	si___gCamZ = camera->y << 14;
	si___gCamY = camera->z << 14;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void Cam_Interpolate( SCamera *a, SCamera *b, s32 interp )
{
	s32
		inverse;

	inverse = (1 << 8) - interp;

	si___gCamTheta = ((a->Yaw * inverse) + (b->Yaw * interp)) >> 8;
	si___gCamThi = ((a->Pitch * inverse) + (b->Pitch * interp)) >> 8;

	si___gCamX = ((a->x * inverse) + (b->x * interp)) << (14-8);
	si___gCamY = ((a->z * inverse) + (b->z * interp)) << (14-8);
	si___gCamZ = ((a->y * inverse) + (b->y * interp)) << (14-8);
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/





/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/


