//*****************************************************************************
//	Include Dependencies
//*****************************************************************************
#include "generic.h"
#include "lfptypes.h"

#include "define.h"
#include "ml.h"
#include "audio.h"

#include "ctrl.h"



int p4f=0,p9f=0;


//*****************************************************************************
// Defines
//*****************************************************************************
#define	TWO_CIRCLES		0
#define COLLIDE_PAUSE	(collide_pause[OFFENSIVE_FOULS])
#define	PUSH_BACK(x)	((((x)->bump_force*((LFPCos((x)->d_angle-(x)->bumped_into_me_angle)+16384)>>7)) *	\
						(100+((x)->plyrROM->ub___pWgt/2)+(x)->plyrROM->ub___pAttrStrength))>>8)


//*****************************************************************************
//	Local Variables
//*****************************************************************************
static SW collide_angle;

int collide_pause[]={
10, 10, 5, 3};

int charge_angles[]={
0x2000, 0x2000 ,0x2000 ,0x2000
};

int charge_pickup[]={
250, 249, 248, 247};



//*****************************************************************************
//	Global Variable Definitions
//*****************************************************************************
int		pbo=0,pbd=0,pbf;



//*****************************************************************************
//	Local Routines
//*****************************************************************************
void	collision_react (playert *plyr, int col_flag);



//*****************************************************************************
//	Routines
//*****************************************************************************

bool collide_sphere_cylinder
		(
		float xo, float yo, float zo,	// Origin
		float xt, float yt, float zt,	// Terminus
		float xs, float ys, float zs,	// Sphere
		float rc,						// Radius of Cylinder
		float rs						// Radius of Sphere
		)
{
	float	xa,ya,za;		// Cylinder Axis
	float	xv,yv,zv;		// Vector to Sphere
	float	xc,yc,zc;		// Cross Product
	float	d;				// Distance
	float	cd=rc+rs;		// Collision distance
	float	l;				// Length

	// Determine & handle origin end cap zone
	xa=xt-xo;
	ya=yt-yo;
	za=zt-zo;

	xv=xs-xo;
	yv=ys-yo;
	zv=zs-zo;

	if (xa*xv+ya*yv+za*zv<0)
	{
		if (xv*xv+yv*yv+zv*zv<cd*cd)
			return TRUE;
		else
			return FALSE;
	}

	// Determine & handle terminal end cap zone
	xa=-xa;
	ya=-ya;
	za=-za;

	xv=xs-xt;
	yv=ys-yt;
	zv=zs-zt;

	if (xa*xv+ya*yv+za*zv<0)
	{
		if (xv*xv+yv*yv+zv*zv<cd*cd)
			return TRUE;
		else
			return FALSE;
	}

	// Handle cylindrical collision (distance from point to line)
	xc=ya*zv-yv*za;
	yc=xv*za-xa*zv;
	zc=xa*yv-xv*ya;
	l=sqrtf(xa*xa+ya*ya+za*za);
	if (!l)
		return FALSE;

	d=(xc*xc+yc*yc+zc*zc)/l;
	if (d<cd*cd)
		return TRUE;
	else
		return FALSE;
}

bool charge_test(SW angle)
{
	if (abs(angle) < charge_angles[OFFENSIVE_FOULS])
	{
	 	return ((possession_ticks>HALF_SEC) && (bhp->basket_dist>0x200000) && (half_flag!=0));
	}

	return FALSE;
}





void new_main(playert *plyr, void *rou)
{
	if (rou==cut_to_basket && plyr->main_routine!=deflt)	// Fix Bug #724, #954
		return;

	if (plyr->main_routine != picked)
	{
		plyr->main_routine = rou;
	}
}

void new_top(playert *plyr, void *rou)
{
	plyr->top_routine = rou;
}





void knocked_back (playert *plyr)
{
	plyr->last_speed= 0;
	plyr->kb_ctr    = 0;
	plyr->flags		&=~HIT;
}



bool post_check(SI new_x, SI new_y)
{
SI post_y;
	//post collision detect
	post_y = new_y - (POST_Y2+0x40000);
	if (post_y > 0)	goto check_x;

	post_y = (POST_Y1-0x40000) - new_y;
	if (post_y > 0)
	{
		check_x:
		return (abs(new_x) < 0xc0000);
	}
	return FALSE;
}


SI collision (playert *plyr, SI x_vel, SI y_vel,SI col_radius)
{
playert *plyr2;
SI  new_x;
SI	new_y;
SI	y_diff;
SI	x_diff;
SI 	temp;
SI	angle;
SI	flag;

	flag	= -1;


	plyr->bumped_into = 0;

	new_x=plyr->xco;
	new_y=plyr->yco;
	
	if (plyr->flags&MOVE_ROOT)
	{
		new_x+=x_vel;
		new_y+=y_vel;
	}
	else
	{
		new_x+=TEMPO(x_vel);
		new_y+=TEMPO(y_vel);
	}

	angle	= plyr->angle;

start_again:

    for (plyr2 = player ;plyr2 < &ball ;plyr2++)
    {
		//check ->no not pointer since project() use copy of playert structure
        if (plyr2->no != plyr->no)
        {
			if (!(plyr2->flags & FALLING))
			{
				y_diff  = plyr2->yco - new_y;
				if (abs(y_diff) < col_radius)
				{
					x_diff  = plyr2->xco - new_x;
					if (abs (x_diff) < col_radius)
					{
	// find distance between path of centres

						temp    = ((y_diff>>4) * LFPCos(angle)) - ((x_diff>>4) * LFPSin(angle));
						temp	>>=10;

						if 	( (abs(temp)) < col_radius)
						{
							if ( (((x_diff>>4) * LFPCos(angle)) + ((y_diff>>4) * LFPSin(angle))) > 0 )
							{
								if (pythag (x_diff,y_diff) < (col_radius))
								{
									plyr2->flags |= COLLIDE;

									if (flag != -1) return (flag + (plyr2->no << 8) + 0x10000);

									flag    = plyr2->no;

									temp    = LFPAsin(temp/(col_radius>>14));
									if (temp >= 0)	temp	+= 0x400;
									else			temp	-= 0x400;

									temp	+= angle;

									new_x   = plyr2->xco - ((LFPCos(temp) * (col_radius>>8)) >> 6);
									new_y   = plyr2->yco - ((LFPSin(temp) * (col_radius>>8)) >> 6);

									if (plyr->flags&MOVE_ROOT)
									{
										x_vel=new_x-plyr->xco;
										y_vel=new_y-plyr->yco;
									}
									else
									{
										x_vel	= INVTEMPO(new_x - plyr->xco);
										y_vel	= INVTEMPO(new_y - plyr->yco);
									}

									plyr->bumped_into = plyr2;
									plyr2->bumped_into_me = plyr;
									plyr->col_flags=plyr2->no;

									goto start_again;
								}
							}
						}
					}
				}
			}
		}
	}

	plyr->xco	= new_x;
	plyr->yco	= new_y;

    return flag;
}



int collide_check(playert *plyr, playert *plyr2, int x_diff, int y_diff, int rad)
{
	if (abs(y_diff) < rad)
	{
		if (abs(x_diff) < rad)
		{
			if (MANHATTAN(x_diff, y_diff) < rad)
			{
				SW col_angle;
				int sin, cos;

				sin = LFPSin(plyr->d_angle)>>6;
				cos = LFPCos(plyr->d_angle)>>6;

				/*are we moving past player ?*/
				if (((cos*x_diff) + (sin*y_diff)) > 0)
				{
					int centre_dist;
					SW  angle_vel;

					/*get distance of path from centre of player ?*/
					centre_dist = ((cos * y_diff) - (sin * x_diff));
					if (abs(centre_dist) < (rad<<8))
					{
						/*get angle between path and line of centres*/
						col_angle     = LFPAsin( centre_dist/ (rad/0x40));
						collide_angle = col_angle;

						if ((plyr2->top_anim == TOP_PICK) && (plyr->pick_ctr))
						{
							angle_vel = 0;
							plyr->pick_ctr--;
							plyr->leg_rate = 0;
							plyr->last_speed = 0;
							return plyr2->no;
						}
						else
						{
							if (plyr==bhp && plyr2->shuffle)
								angle_vel=0x300;
							else
								angle_vel = 0x400;
						}

						if (col_angle>= 0)  col_angle += angle_vel;
						else				col_angle -= angle_vel;

						col_angle += plyr->d_angle;

						plyr->xco += x_diff - (LFPCos(col_angle) * (rad>>14));
						plyr->yco += y_diff - (LFPSin(col_angle) * (rad>>14));

						plyr->bumped_into				= player+plyr2->no;
						plyr2->bumped_into_me			= player+plyr->no;
						plyr->col_flags=plyr2->no;

						return plyr2->no;
					}
				}
			}
		}
	}

	return -1;
}



/* *************************************************************************/
/* run_collision*/
/* *************************************************************************/
/* move a player (special case of above) checks for gaurding positions*/
/* *************************************************************************/
/* Inputs-  pointer to player data, x_vel, y_vel, and collision radius*/
/* Output   -1 no collision*/
/*			0-9 number of player collided with*/
/* *************************************************************************/
SI run_collision (playert *plyr, SI x_vel, SI y_vel)
{
	SI		x_diff;
	SI		y_diff;
	SB		col_no;
	SB		cn;
	playert	*plyr2;
	int		ox,oy;		// Old position

	plyr->bumped_into = 0;

	ox=plyr->xco;
	oy=plyr->yco;

	if (plyr->flags&MOVE_ROOT)
	{
		plyr->xco+=x_vel;
		plyr->yco+=y_vel;
	}
	else
	{
		plyr->xco += TEMPO(x_vel);
		plyr->yco += TEMPO(y_vel);
	}

	if (project_flag)	return -1;

#if 1
	if (post_check(plyr->xco, plyr->yco))
	{
		start_fall(plyr, (SW)plyr->angle);
		return -1;
	}
#endif
	col_no = -1;

	for (plyr2 = player; plyr2 < player+10; plyr2++)
	{
		//check ->no not pointer since project() uses copy of playert structure
		if (plyr2->no == plyr->no)
			continue;

		if (plyr2->flags & FALLING)
			continue;

		if (plyr2->zco != 0)
			continue;

		y_diff = (plyr2->yco - plyr->yco);
		x_diff = (plyr2->xco - plyr->xco);
#if TWO_CIRCLES
		if (plyr->no==bhp->no && plyr2->shuffle)	// Handle shuffle collisions here
			cn=shuffle_check(plyr,plyr2,x_diff,y_diff);
		else
#endif
			cn=collide_check(plyr, plyr2, x_diff, y_diff, COL_DI);

#if 1
		if (cn!=-1)
			if (col_no!=plyr->pickee || col_no==-1)	// Ensure no override of pickee
				col_no=cn;
#endif
	}

	if (plyr->hand_check_flag && plyr->bumped_into && plyr->bumped_into == plyr->bumped_into_2)
	{
		playert *bi;

		bi=plyr->bumped_into;
		if (!(bi->xvel|bi->yvel))
		{
			plyr->xco=ox;
			plyr->yco=oy;
			plyr->xvel=0;
			plyr->yvel=0;
		}
	}

	if (plyr->bumped_into!=plyr->bumped_into_me)
		push_back(plyr,plyr->bumped_into);
	push_back(plyr,plyr->bumped_into_me);

	return col_no;
}

void push_back
		(
		playert	*plyr,
		playert	*plyr2
		)
{
	int	s,s2;		// Strengths of plyr & plyr2, respectively
	int	dx,dy;		// Pushback distance
	int	ds;			// Delta between the two s values

		int t,v,t2,v2;
		SW	u,u2;


	if (!plyr2)
		return;

	if (plyr->force>224 && plyr->no!=bhp->no)
		return;
		
	if (plyr->no==bhp->no)
		if (!(plyr2->shuffle || plyr2->leg_routine==leg_deflt))
			return;

	if (plyr2->no==bhp->no)
		if (!(plyr->shuffle || plyr->leg_routine==leg_deflt))
			return;

	if (plyr->no==bhp->no && !(plyr->xvel|plyr->yvel))
		return;

	if (plyr->control)
		plyr->bump_force-plyr2->force;

	if (plyr2->control)
		plyr2->bump_force=plyr2->force;

	if (plyr->no==4)
		pbf=plyr->force;




	if (!plyr->control)
	{
		if (player[plyr->marker].basket_dist < 0x200000)
			plyr->bump_force = 0x100;
		else
		{
			plyr->bump_force = 0x100 + ((0x200000 - player[plyr->marker].basket_dist) >> 14);
			if (plyr->bump_force<0)
				plyr->bump_force=0;
		}
	}




	dx=plyr2->xco-plyr->xco;
	dy=plyr2->yco-plyr->yco;
	plyr->bumped_into_me_angle=LFPAtn(dx,dy);

		t=plyr->bump_force;
		u=plyr->d_angle-plyr->bumped_into_me_angle;
		v=(100+(plyr->plyrROM->ub___pWgt/2)+plyr->plyrROM->ub___pAttrStrength);
		s=(t*((16384+LFPCos(u>>3))>>7)*v)>>8;

		t2=plyr2->bump_force;
		u2=plyr2->d_angle+plyr2->bumped_into_me_angle;
		v2=(100+(plyr2->plyrROM->ub___pWgt/2)+plyr2->plyrROM->ub___pAttrStrength);
		s2=(t2*((16384+LFPCos(u2>>3))>>7)*v2)>>8;

	//s=PUSH_BACK(plyr);
	//s=(s>=0)? s : -s;
	//if (plyr->no==bhp->no && !plyr->shuffle)
	//	s>>=1;

	//s2=PUSH_BACK(plyr2);
	//s2=(s2>=0)? s2 : -s2;
	//if (plyr2->no==bhp->no && !plyr2->shuffle)
	//	s2>>=1;


	if (plyr2->shuffle && plyr->no==bhp->no)
		s2=s2*3/2;
	//else if (plyr->shuffle && plyr2->no==bhp->no)
	//	s=s*3/2;
/*
	if (plyr->shuffle && plyr->no!=bhp->no)
		if (plyr->force==0)
			DebugCode();

	
*/

	
	if (plyr->no==9)
	{
		p9f=s;
		p4f=s2;
		if (s<s2)
			ds=2;
	}

	if (plyr->no==9 && s2==0)
		ds=77;

	if (s2>s)	// Is the other player over-powering me?
	{
	#if defined(DEBUG)
		if (plyr->no==bhp->no)
			pbo++;
		else
			pbd++;
	#endif

		ds=s2-s;
		if (plyr->no==bhp->no && plyr->basket_dist<0x700000)
			ds=(ds*((0x700000-plyr->basket_dist)>>16))/0x30;

		dx=LFPCos(plyr->bumped_into_me_angle)*ds>>14;
		dy=LFPSin(plyr->bumped_into_me_angle)*ds>>14;

		plyr->xco-=dx;
		plyr->yco-=dy;
	}

	PlotLine(plyr->xco,plyr->yco,0,plyr2->xco,plyr2->yco,0,255,0,255);
}



void dunk_collision (playert *plyr)
{
SI  new_x, new_y, old_x, old_y;
SI 	x_diff;
SI	y_diff;
SW  move_angle;
SW  cos,sin;
SI  centre_dist;
playert *plyr2;
SW  col_angle;

	plyr->bumped_into = 0;

    old_x = plyr->xco;
    old_y = plyr->yco;

    plyr->xco = old_x + TEMPO(plyr->xvel);
    plyr->yco = old_y + TEMPO(plyr->yvel);

	if (project_flag)	return;

	move_angle = LFPAtn(plyr->xvel, plyr->yvel);

	for (plyr2 = player+(plyr->base^5); plyr2 < player+(plyr->base^5)+5; plyr2++)
	{
		//check ->no not pointer since project() use copy of playert structure
		if (!(plyr2->flags & FALLING))
		{
			if (plyr2->zco == 0)
			{
				if ((plyr2->last_speed==0) || (plyr2->shuffle))
				{
					y_diff = (plyr2->yco - new_y);
					if (abs(y_diff) < DUNK_DI)
					{
						x_diff = (plyr2->xco - new_x);

						if (abs(x_diff) < DUNK_DI)
						{
							if (MANHATTAN(x_diff, y_diff) < DUNK_DI)
							{
								sin = LFPSin(move_angle);
								cos = LFPCos(move_angle);
								x_diff >>= 8;
								y_diff >>= 8;

								/*are we moving past player ?*/
								if (((cos*x_diff) + (sin*y_diff)) > 0)
								{
									/*get distance of path from centre of player ?*/
									centre_dist = ((cos * y_diff) - (sin * x_diff)) >> 14;
									if (abs(centre_dist) < 0xa00)					//(COL_RADIUS>>8))
									{
										int charge, hack, nothing, r;

										/*get angle between path and line of centres*/
//phil										col_angle     = LFPAsin( centre_dist/ (DUNK_DI>>22) );
										col_angle     = LFPAsin( centre_dist/ DUNK_DI );
										collide_angle = col_angle;

										start_fall(plyr2, (SW)(move_angle^0x8000));

										if (plyr2->control)	CtrlMotorOn(plyr2->control-1, 10);
										if (plyr->control)	CtrlMotorOn(plyr->control-1, 10);

										hack   = 0;
										charge = 0;
										nothing= 8-DEFENSIVE_FOULS-OFFENSIVE_FOULS;

										hack   = plyr2->plyrROM->ub___pAttrFoul*DEFENSIVE_FOULS;
										charge = plyr->plyrROM->ub___pAttrFoul*OFFENSIVE_FOULS;

										if (plyr2->basket_dist<0x140000)	charge=0;

										r = RANDOM(hack+charge+nothing);

										if (r<hack)
										{
											if (DEFENSIVE_FOULS)
											{
												if (CAMERAS==1)		CameraSpecialEnd();
												FoulSet(dunk_collision,plyr2, REACHING_IN);
											}
										}
										else if	(r<(hack+charge))
										{
											if (OFFENSIVE_FOULS)
											{
												if (CAMERAS==1)		CameraSpecialEnd();
												FoulSet(dunk_collision,plyr, CHARGING);
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}


















/* *************************************************************************/
/* frozen()*/
/* *************************************************************************/
/* collision handling routine for player that has been run in to*/
/* *************************************************************************/
/* Inputs   - ptr to player data*/
/* Output   -*/
/* *************************************************************************/

void frozen(playert *plyr)
{

/*continue dribble if ball handler*/

	if ((plyr->no == ball_handler) & (possession_flag))
	{
//		plyr->old_main_rou	= dribble;
	}

	if (--plyr->freeze_ctr == 0)
	{
		plyr->flags		    &=~HIT;
		plyr->last_speed    >>= 1;
	}
}



/* *************************************************************************/
/* collision_react()*/
/* *************************************************************************/
/* collision handling routine for player that run in to someone else*/
/* *************************************************************************/
/* Inputs   - ptr to player data*/
/* Output   -*/
/* *************************************************************************/

void collision_react (playert *plyr, int col_flag)
{
int x_diff;
int y_diff;
SW  angle;
playert *reactr;

/*ball handler involved ?*/
	reactr	= &player[col_flag];
	x_diff	= reactr->xco - plyr->xco;
	y_diff	= reactr->yco - plyr->yco;

	angle	= reactr->col_angle	= LFPAtn (x_diff,y_diff);

//ball handler involved
	if ((picked_up_flag) && (picker_up == reactr->no))
		return;

	if (!possession_flag)
		return;

//can't knock ball handler over
	if (reactr->no == ball_handler)
		return;

	if (plyr->no != ball_handler)
		return;

	if (plyr->force < 0xc0)
		return;

//both on same team ?
	if (!(reactr->team^plyr->team))
		return;

//both CPU controlled players ?
	if (! ((plyr->control) || (reactr->control)) )
		return;

//defending in CTRL_Z mode ?

	if ((reactr->shuffle) || ((reactr->xvel|reactr->yvel) == 0))
		if (charge_test(collide_angle))
			if ((possession_ticks>HALF_SEC) && (bhp->basket_dist>0x200000) && (half_flag))
				if (BALL_HANDLER)
				{
					if (!project_flag)
					{
						int f;

						if (reactr->control)	CtrlMotorOn(reactr->control-1, 10);
						if (plyr->control)		CtrlMotorOn(plyr->control-1, 10);

						f = ReadRnd250() + charge_pickup[OFFENSIVE_FOULS];

						if (f<250)
						{
							start_fall(reactr, (SW)(angle-0x8000));
							if (reactr->top_anim == TOP_FALLB1)
								FoulSet(collision_react,plyr, CHARGING);
						}
					}
					return;
				}

}
