//************************************************************************
//	Dependencies
//************************************************************************
#include "generic.h"
#include "lfptypes.h"
#include "define.h"
#include "ml.h"

#include "playgfx.h"
#include "audio.h"
#include "comment.h"


#include "gamefx.h"

extern u8
	*GameBank;


//************************************************************************
//	Defines
//************************************************************************
#define HUMAN_REB_TO_TIME	2				// Ticks for human response delay
#define CPU_REB_TO_TIME		(5*TIME_FACTOR)	// Ticks for cpu response delay
#define REB_CATCH_DELTA		2				// Ticks preceding grab for ball height
#define	REB_CHK_SPD			0x18000			// Speed of player moving to rebound



//************************************************************************
//	Global Data References
//************************************************************************
extern UB *lay_up_made;



//************************************************************************
//	Gobal Data Declarations
//************************************************************************
int		picker_up;



//************************************************************************
//	Routines
//************************************************************************


void grab_ball_rou
		(
		playert	*plyr
		)
{
	int	dx,dy;			// Difference between current and desired position
	int	speed;			// Distance covered this frame is the speed

	CallChainP(grab_ball_rou);

	// Move the player
	if (plyr->ctr2>REB_CATCH_DELTA)
	{
		dx=(plyr->target_x-plyr->xco)/plyr->ctr2;
		dy=(plyr->target_y-plyr->yco)/plyr->ctr2;
		speed=MANHATTAN(dx,dy);

		plyr->xco+=dx;
		plyr->yco+=dy;
		running_animate(plyr,speed);
		plyr->ctr2--;
	}
	else	// Time over
	{
		if (rebound_flag)	// Ball still available, catch
		{
			SetIkBlendSpeed(grab_ball_rou,plyr,-0x100/4);
			start_next_anim2(plyr, 5);
			if (!project_flag)
			{
				if (plyr->team == player[last_handler].team)
					stats_o_reb(plyr);
				else
					stats_d_reb(plyr);
			}
			catch_ball(plyr);

			SetMain(grab_ball_rou,plyr,intercept_catch);
			plyr->top_routine  = NULL;
			get_new_controls(&ai_team[plyr->team], plyr);
			mask_shoot();
			reset_play();
		}
		else	// Some other bastard caught the ball first
		{
			SetMain(grab_ball_rou,plyr,deflt);
			plyr->leg_routine  = leg_deflt;
			plyr->top_routine  = top_deflt;
			SetIkBlendSpeed(grab_ball_rou,plyr,-0x100/4);
		}
	}
}



void grab_ball_start
		(
		playert	*plyr,
		int		t			// Number of frames in future to be at ball
		)
{
	int		dt;				// Amount of time until succesful rebound
	int		bx,by,bz;		// Ball's position in the future
	int		dx,dy;			// Delta x,y between player and ball
	int		dist;			// Distance between player and ball

	void	(*omr)(playert *);	// Old main routine
	void	(*olr)(playert *);	// Old leg routine

	CallChainP(grab_ball_start);

	while(ball_cos[t*3+2]<0x150000)
	{
		t--;
	}

	bx=ball_cos[t*3];
	by=ball_cos[t*3+1];
	bz=ball_cos[t*3+2];

	dt=t+proj_time-lp_ctr;
	if (!dt)
		return;

	dx=plyr->xco-bx;
	dy=plyr->yco-by;
	dist=MANHATTAN(dx,dy)>>8;

	dx=(dx/dist)<<11;
	dy=(dy/dist)<<11;
	//dist=MANHATTAN(dx,dy);

	plyr->target_x = bx+dx;
	plyr->target_y = by+dy;
	plyr->ctr2     = dt;
/*
	crossX=plyr->target_x;
	crossY=plyr->target_y;
*/
	status_var1 = dt;

	omr=plyr->main_routine;
	SetMain(grab_ball_start,plyr,grab_ball_rou);
	olr=plyr->leg_routine;
	plyr->leg_routine=NULL;

	check_routines(plyr);
	project(plyr,dt,FALSE);
	if (t+proj_time>end_proj_time)
		return;

	if (!ik_simple(&DUMMY,bx/1024, ball_cos[(t-REB_CATCH_DELTA)*3+2]/1024,by/1024))
	{
		plyr->main_routine=omr;
		plyr->leg_routine=olr;
		return;
	}

	start_blend_anim2(plyr,DUMMY.top_anim,dt);
	SetIkBlendSpeed(grab_ball_start,plyr,0x100/dt);
	plyr->top_routine=NULL;

	return;
}



void pick_a_chaser(void)
{
playert *plyr;
playert *closest_plyr;
int     nearest_dist;


	nearest_dist = 0x7fffffff;
	closest_plyr = NULL;

	// Do not pick a chaser if someone is already going for reb
	if (rebound_flag && cur_team->reb_plyr)
		return;

	// Find the closest player to go after ball
	for (plyr = cur_team->plyr1; plyr < cur_team->plyr1+5; plyr++)
	{
		if (plyr->control == 0)
		{
			if (plyr->ball_dist < nearest_dist)
			{
				if (plyr->main_routine == deflt)
				{
					closest_plyr = plyr;
					nearest_dist = plyr->ball_dist;
				}
			}
		}
	}

	if (closest_plyr)
	{
		cur_team->chaser = closest_plyr;
	}
}


bool ball_pickup_check(playert *plyr)
{
int x_vel, y_vel;
int x_diff, y_diff;
int x_diff2;
SW  a_diff;

int speed;
int time;
SW  angle;

	CallChainP(ball_pickup_check);

	//if ((plyr->control!=0) & (in_play_flag))

	if ((in_play_flag) && (!picked_up_flag))
	{
		if (plyr->ball_dist < 0x200000)
		{
			if (ball.zco < 0x200000)
			{
				//find nearest approach
				x_diff = (plyr->xco - ball.xco) >> 8;
				y_diff = (plyr->yco - ball.yco) >> 8;

				x_vel = plyr->xvel - ball.xvel;
				y_vel = plyr->yvel - ball.yvel;

				speed = MANHATTAN(x_vel, y_vel);
				if (speed)
				{
					angle = LFPAtn(x_vel, y_vel);

					x_diff2 = ((x_diff * LFPCos(angle)) + (y_diff * LFPSin(angle))) >> 6;

					if (x_diff2 < 0)
					{
						if (x_diff2 > -1200*4*plyr->scale)
						{
							//y_diff2 = (-(x_diff * LFPSin(angle)) + (y_diff * LFPCos(angle))) >> 6;

							time = -x_diff2/speed;
							if (time > 10)	time = 10;			//stop real slow pick up
							if (time < 5)	time = 5;			//stop divide by zero

							x_diff = ball.xco + (ball.xvel*time) - plyr->xco;
							y_diff = ball.yco + (ball.yvel*time) - plyr->yco;

							angle  = LFPAtn(x_diff, y_diff);
							a_diff = angle - plyr->angle;

							if (abs(a_diff) < 0x1666 || (abs(a_diff<0x3000) && plyr->ball_dist<0x80000 ) )
							{
								int stick;
								int zco;
								int xv, yv;

								get_future_ball_cos(time, &xv, &yv ,&zco);

								if (abs(xv) < SIDE_LINE)
								{
									if (abs(yv) < BASE_LINE)
									{
										stick = (220*4*plyr->scale)>>14;

										x_diff += stick * LFPSin(plyr->angle);
										y_diff += stick * LFPCos(plyr->angle);
										xv     = x_diff/(time+1);
										yv     = y_diff/(time+1);

										if (MANHATTAN(xv, yv) < 0x30000)
										{
											plyr->top_routine  = NULL;
											plyr->leg_routine  = NULL;


											plyr->xvel = xv;
											plyr->yvel = yv;

											if ((zco < 0x80000) && (ball.zco < 0x100000))
											{
												//use bend down pick up animation
												if (a_diff < 0)
												{
													start_anim2(plyr, TOP_RPICKUP1, time);
												}
												else
												{
													start_anim2(plyr, -TOP_RPICKUP1, time);
													stick = -stick;
												}

												plyr->angle = angle;		//-0x666;

												if ((((plyr->leg_frame>>8)+8) % 20) < 20)	start_blend_leg_anim(plyr, LEG_PICKUP1);
												else										start_blend_leg_anim(plyr,-LEG_PICKUP1);
												plyr->leg_rate = plyr->top_rate;

												SetMain(ball_pickup_check,plyr,run_pickup_rou1);
											}
											else
											{
												int top_z;

												top_z = plyr->scale * 1600 * 4;

												//use dribble animation
												if (a_diff < 0)
												{
													start_blend_anim(plyr, -TOP_HIGH_DRIB);
													stick = -stick;
												}
												else
												{
													start_blend_anim(plyr, TOP_HIGH_DRIB);
												}
												plyr->top_rate = 0;
												plyr->top_blend_speed = 0x100/time;

												if (player[10].zco < top_z)
												{
													top_z = player[10].zco/(top_z>>8);

													if (ball.zvel > 0)		plyr->top_frame = 20*(0x100+top_z);
													else					plyr->top_frame = 20*(0x100-top_z);
												}

												//BRK;
												SetMain(ball_pickup_check,plyr,dribble_pickup_rou1);
												plyr->top_routine=top_deflt;
												plyr->leg_routine=leg_deflt;
											}

											SetPickedUpFlag(ball_pickup_check,TRUE);
											picker_up      = plyr->no;

											return TRUE;
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return FALSE;
}



void run_pickup_rou1(playert *plyr)
{
int rate;
int x, y;

	CallChainP(run_pickup_rou1);

	plyr->xco += plyr->xvel;
	plyr->yco += plyr->yvel;

	if ((possession_flag) || (!in_play_flag))
	{
		//someone got there 1st
		SetPickedUpFlag(run_pickup_rou1,FALSE);
		set_default(plyr);
		return;
	}


	if (end_animation_test(plyr))
	{
		if (possession_flag)	// Someone else already picked up the ball
		{
			BRK;
		}

		SetPickedUpFlag(run_pickup_rou1,FALSE);

		check_rebound(plyr);
		get_new_controls(&ai_team[plyr->team], plyr);
		catch_ball(plyr);

		rate = plyr->top_rate;
		start_next_anim(plyr);
		plyr->top_rate = rate;

		rate = plyr->leg_rate;
		start_next_leg_anim(plyr);
		plyr->leg_rate = rate;

		//project_forward 2.0m to see if out of bounds
		x = plyr->xco + (LFPCos(plyr->angle)*0x80);
		y = plyr->yco + (LFPSin(plyr->angle)*0x80);

		//set up half flag
		if (!project_flag)	follow_player(&ball);

		if (plyr->no > 4)	y = -y;
		//don't check for halfway line if half_flag not set
		if (!half_flag)		y = abs(y);

		if ((abs(x) > SIDE_LINE) || (y > BASE_LINE) || (y < 0))
		{
			start_next_anim(plyr);
			start_next_leg_anim(plyr);
			SetMain(run_pickup_rou1,plyr,run_pickup_rou3);
		}
		else
		{
			SetMain(run_pickup_rou1,plyr,run_pickup_rou2);
		}
	}
}


void run_pickup_rou2(playert *plyr)
{
	CallChainP(run_pickup_rou2);

	run_collision(plyr, plyr->xvel, plyr->yvel);

	if (BALL_HANDLER)
	{
		if (end_animation_test(plyr))
		{
			SetMain(run_pickup_rou2,plyr,dribble);
			plyr->top_routine  = dribble_animate;
			start_leg_anim(plyr, LEG_RUN);
			plyr->leg_routine  = running;
			plyr->last_speed  = 0x800;
		}
	}
}


void run_pickup_rou3(playert *plyr)
{
	CallChainP(run_pickup_rou3);

	if (end_animation_test(plyr))
	{
		SetMain(run_pickup_rou3,plyr,stand_with_ball);
		start_leg_anim(plyr, LEG_RDY);
		start_anim(plyr, TOP_STAND_BALL);
		plyr->leg_routine  = leg_deflt;
	}
}

void dribble_pickup_rou1(playert *plyr)
{
	CallChainP(dribble_pickup_rou1);

	if (!in_play_flag)
	{
		SetPickedUpFlag(dribble_pickup_rou1,FALSE);
		set_default(plyr);
		return;
	}

	plyr->xco += plyr->xvel;
	plyr->yco += plyr->yvel;

	// If someone else beat me to the ball, just go back to normal
	if (possession_flag)
	{
		set_default(plyr);
		return;
	}

	if (plyr->top_blend_ctr == 0)
	{
		int x, y;

		check_rebound(plyr);

		get_new_controls(&ai_team[plyr->team], plyr);
		catch_ball(plyr);

		//project_forward 2.0m to see if out of bounds
		x = plyr->xco + (LFPCos(plyr->angle)*0x80);
		y = plyr->yco + (LFPSin(plyr->angle)*0x80);

		//set up half flag
		follow_player(&ball);
		if (plyr->no > 4)	y = -y;
		//don't check for halfway line if half_flag not set
		if (!half_flag)		y = abs(y);

		if ((abs(x) > SIDE_LINE) || (y > BASE_LINE) || (y < 0))
		{
			SetMain(dribble_pickup_rou1,plyr,stand_with_ball);
			plyr->top_routine  = NULL;
			plyr->leg_routine  = leg_deflt;
		}
		else
		{
			SetMain(dribble_pickup_rou1,plyr,dribble);
			plyr->top_routine  = dribble_animate;
			plyr->leg_routine  = running;
		}
	}
}


void set_default(playert *plyr)
{
	SetMain(set_default,plyr,deflt);
	plyr->leg_routine  = leg_deflt;
	plyr->top_routine  = top_deflt;

	start_blend_anim(plyr, TOP_RDY);
	start_blend_leg_anim(plyr, LEG_RDY);

	if (plyr->ikBlend)
		plyr->ikBlendSpeed=-0x100/4;
}


void turn_over(void)
{
	assister = -1;
	new_shot_clock();
	set_fast_break();
	stats_TO(bhp);
}


void check_rebound(playert *plyr)
{
	CallChainP(check_rebound);

	//got ball
	if (rebound_flag)
	{
		//am I in my own half?
		if ((plyr->no > 4) ^ (ball.yco > 0))
		{
			//offensive rebound
			stats_o_reb(plyr);
			reset_play();
		}
		else
		{
			//defensive rebound
			new_shot_clock();
			stats_d_reb(plyr);
			set_fast_break();
		}
	}
	//was it a steal?
	else if (((ball_handler-5)^(plyr->no-5)) < 0)
	{
		if (steal_flag)
		{
			//steal
			stats_steal(plyr);
			if (plyr->no<5)
			{
//old				AudioPlayCrowdSfx(AUDIO_CROWD_CHEER + ((ReadRnd250()*3)>>8));
			}
		}
		if (!ball.no2)
			turn_over();
	}
	else
	{
		reset_play();
	}

	steal_flag = FALSE;
}




void rebound_positions(void)
{
SW back_angle;
SW angles[4];
int dist[10];
int order[4];
int i;

int ideal_x, ideal_y;

bool sort_flag;

playert *plyr;

int ddist;

	//sort according to proximity to basket

	//get two closest players to basket from each team


	ddist=0x7fffffff;
	for (i=0; i<5; i++)
	{
		if (player[i].basket_dist<ddist)
		{
			ddist = player[i].basket_dist;
			order[0] = i;
		}
	}

	ddist=0x7fffffff;
	for (i=0; i<5; i++)
	{
		if ((order[0] != i) && (player[i].basket_dist<ddist))
		{
			ddist = player[i].basket_dist;
			order[1] = i;
		}
	}


	ddist=0x7fffffff;
	for (i=5; i<10; i++)
	{
		if (player[i].basket_dist<ddist)
		{
			ddist = player[i].basket_dist;
			order[2] = i;
		}
	}

	ddist=0x7fffffff;
	for (i=5; i<10; i++)
	{
		if ((order[2] != i) && (player[i].basket_dist<ddist))
		{
			ddist = player[i].basket_dist;
			order[3] = i;
		}
	}


	//get ideal rebounding position  based on ball's angle
	ideal_y = current_basket_y;
	if (!ft_flag)	ideal_y -= (LFPSin(bhp->basket_angle) * 0x40);
	ideal_x = LFPCos(bhp->basket_angle) * 0x40;

	for (i=0; i<4; i++)
	{
		dist[order[i]] = MANHATTAN(player[order[i]].xco - ideal_x,
							player[order[i]].yco - ideal_y);
	}


	do{
		sort_flag = FALSE;

		for(i=0;i<(4-1);i++)
		{
			if (dist[order[i]] > dist[order[i+1]])
			{
				int swap;

				swap       = order[i+1];
				order[i+1] = order[i];
				order[i]   = swap;

				sort_flag  = TRUE;
			}
		}
	}
	while(sort_flag == TRUE);

	//nearest
	//rebounder = order[0];

	if (last_handler > 4)	back_angle = -0x8000;
	else					back_angle = 0;


	for (i=0; i<4; i++)
	{
		//check to see if angle inteferes with any closer players slot

		int dist;
		SW  angle;

		angle = player[order[i]].basket_angle;
		dist  = 0x40 + (shot_dist>>18);

		if (ft_flag)
		{
			if (i<2)
			{
				if ((current_basket_y > 0) ^ (abs(angle) < 0x4000))
				{
					angle -= 0x2800;
				}
				else
				{
					angle += 0x2800;
				}

				//slow these guys down
				player[order[2]].freeze_ctr = 8;
				player[order[3]].freeze_ctr = 8;
			}
		}


		if (i)
		{
			int j;

			bool moved_flag = FALSE;

			for (j=0; j<i; j++)
			{
				if (abs(angle - angles[j]) < 0x2000)
				{
					if (!moved_flag)
					{
						angle = angles[j] + (((angle-angles[j]) > 0) ? 0x2000: -0x2000);
						if ((angle-back_angle) & 0x8000)
						{
							if ((angle-back_angle+0x4000) & 0x8000)	angle -= 0x4000;
							else				 					angle += 0x4000;
						}
						moved_flag = TRUE;
					}
					else
					{
						dist = dist+0x40;
						player[order[i]].basket_angle;
					}
				}
			}
		}

		angles[i] = angle;

		player[order[i]].target_x = -LFPCos(angle) * dist;
		player[order[i]].target_y = current_basket_y - (LFPSin(angle) * dist);

		player[order[i]].look_at_ball_ctr = ball.ctr2+ONE_SEC;
	}



	//find two attackers furthest from basket
	if (current_basket_y > 0)
	{

		ddist=0;
		for (i=0; i<5; i++)
		{
			if (player[i].basket_dist>ddist)
			{
				ddist = player[i].basket_dist;
				order[0] = i;
			}
		}

		ddist=0;
		for (i=0; i<5; i++)
		{
			if ((order[0] != i) && (player[i].basket_dist>ddist))
			{
				ddist = player[i].basket_dist;
				order[1] = i;
			}
		}
	}
	else
	{
		ddist=0;
		for (i=0; i<5; i++)
		{
			if (player[i].basket_dist>ddist)
			{
				ddist = player[i].basket_dist;
				order[0] = i;
			}
		}

		ddist=0;
		for (i=0; i<5; i++)
		{
			if ((order[0] != i) && (player[i].basket_dist>ddist))
			{
				ddist = player[i].basket_dist;
				order[1] = i;
			}
		}
	}

	player[order[0]].target_y = 0;
	player[order[1]].target_y = 0;
}




bool rebound_test(playert *plyr)
{
int x_diff;
int y_diff;
int z_diff;

int xco, yco, zco;

int z_vel;

int t, lt;
int start_time, end_time;

int h_dist;
int next_anim;

int		speed;
bool	low_flag=FALSE;
int		threshold;
int		to_time;
teamt	*tm;
SW		da;

	CallChainP(rebound_test);

	if (!in_play_flag)
		return FALSE;

	if (rebound_time==0)				return FALSE;
	if (plyr->ball_dist>0x300000)		return FALSE;

	threshold = (plyr->control) ? 10: CPU_RT;
	if ((rebound_time-lp_ctr) > threshold)	return FALSE;

	xco = plyr->xco;
	yco = plyr->yco;
	zco = (plyr->scale * 9551);				//2.5m from ground

	to_time = (plyr->control) ? HUMAN_REB_TO_TIME: CPU_REB_TO_TIME;

	start_time = rebound_time;
	if ((lp_ctr+to_time)>rebound_time)		start_time = lp_ctr+to_time;

	start_time -= proj_time;				//lt is lookup-time in ball_cos
	end_time   = start_time+ONE_SEC/2;

	if (end_time >= (end_proj_time-proj_time))		end_time = end_proj_time-proj_time-1;

	tm=&ai_team[plyr->team];

	for (lt = start_time+1; lt<end_time; lt += 1)
	{
		if (ball_cos[lt*3 +2] > 0x400000)	// If the ball is too high, check next frame
			continue;

		z_diff = ball_cos[lt*3 +2] - zco;
		if (z_diff <= 0 )
			//return FALSE;
			low_flag=TRUE;

		t = lt + proj_time - (lp_ctr + to_time);

		z_vel  = (z_diff/t)
				 + ((t-1)*(LITTLE_G/2));

		if (z_vel < (LITTLE_G*12) && z_vel<(t+2)*LITTLE_G)	// Within 2 ticks of peak
		{
			x_diff = ball_cos[lt*3] - xco;
			y_diff = ball_cos[lt*3 +1] - yco;

			h_dist = MANHATTAN(x_diff, y_diff);

			speed=(x_diff*plyr->xvel+y_diff*plyr->yvel)/h_dist;
			if (speed<REB_CHK_SPD)
				speed=REB_CHK_SPD;

			if ( h_dist < t*speed ) //0x12000) )
			{
				// Scrap computer-controlled longer-shot player
				if (!plyr->control && tm->reb_plyr && lt>tm->reb_lt)
					return FALSE;

				// If ball is low, cannot grab ball if not facing it
				if (low_flag)
				{
					da=LFPAtn(x_diff,y_diff)-plyr->angle;
					if (abs(da)>0x1800)
						return FALSE;

					if (lt+proj_time-lp_ctr>(ONE_SEC/2))
						return FALSE;

				}

				// This is the guy
				tm->reb_plyr=plyr;
				tm->reb_lt=lt;
				tm->reb_t=t;
				tm->reb_to=to_time;
				tm->reb_low_flag=low_flag;
				return TRUE;


				//rebound_start(plyr,lt,t,to_time);
			}
		}
	}

	return FALSE;
}



void rebound_team_check
		(
		int		no
		)
{
	int		s,e;
	int		i;
	teamt	*t;

	t=&ai_team[no];
	for (i=5*no; i<5*(no+1); i++)
		if (player[i].main_routine==deflt)
			rebound_test(&player[i]);

	if (t->reb_plyr)
		if (!t->reb_plyr->control)
			rebound_start(t->reb_plyr,t->reb_lt,t->reb_t,t->reb_to,t->reb_low_flag);
}



void rebound_start
		(
		playert	*plyr,
		int		lt,
		int		t,
		int		to_time,
		bool	low_flag
		)
{
	int		xco,yco,zco;			// Position of player
	int		x_diff,y_diff,z_diff;	// Difference between the position of the ball and the player
	int		next_anim;

	xco = plyr->xco;
	yco = plyr->yco;
	zco = (plyr->scale * 9551);				//2.5m from ground

	x_diff = ball_cos[lt*3] - xco;
	y_diff = ball_cos[lt*3 +1] - yco;
	z_diff = ball_cos[lt*3 +2] - zco;

	plyr->angle2 = LFPAtn( x_diff, y_diff);

	next_anim = TOP_REB_UP;


	// Reach out for that sucker if it is low
	if (low_flag)
	{
		grab_ball_start(plyr,lt);
		return;
	}

	if (ATTACKING)
	{
		z_diff += zco;
		if (z_diff >= BASKET_Z)
		{
			x_diff += plyr->xco;
			y_diff += plyr->yco;
			y_diff -= current_basket_y;

			//am I close enough to basket? (within 1m)
			if (MANHATTAN(x_diff, y_diff) < (500<<10))
			{
				if (plyr->plyrROM->ub___pAttrRebOff > 80)
				{
					if (abs(plyr->yco) > BASKET_Y)
					{
						SW a_diff;

						a_diff = (plyr->yco > 0) ? 0xc000:0x4000;
						a_diff = plyr->basket_angle - a_diff;

						next_anim = (a_diff>0) ? -TOP_TIP_UP:TOP_TIP_UP;
					}
					else
					{
						next_anim = (lp_ctr & 1) ? TOP_TIP_UP:-TOP_TIP_UP;
					}
					BigPoints=HotSpot_Check(plyr);
				}
			}
		}
	}

	//don't let any CPU players grab a human rebound
	if (plyr->control)	human_reb_flag = TRUE;

	plyr->ctr2	 = lt+proj_time;

	//printf("lt %d, pt %d, lp %d, t %d, tot %d\n", lt, proj_time, lp_ctr, t, to_time);

	start_blend_anim2(plyr, next_anim, t+to_time);
	plyr->top_blend_speed = 0x100/to_time;
	start_blend_leg_anim2(plyr, LEG_REB_TO, to_time);
	SetMain(rebound_start,plyr,rebound_rou1);
	plyr->leg_routine  = NULL;
	plyr->top_routine  = NULL;
}



void rebound_rou1(playert *plyr)
{
int time;
int	x_diff;
int	y_diff;
int	z_diff;
int next_top_anim;
int anim, fr, lanim, lfr;

	CallChainP(rebound_rou1);

	//on ground

	//if caught or out of bounds stop
	if ((possession_flag) || (!in_play_flag))
	{
		set_default(plyr);
		return;
	}

	//face ball
	turn_to(plyr, (SW)plyr->angle2);

	if (end_leg_animation_test(plyr))
	{
		next_top_anim = (plyr->top_anim>0) ? plyr->top_anim+1: plyr->top_anim-1;

		if (abs(next_top_anim) == TOP_TIP_SHOT)
		{
			SetMain(rebound_rou1,plyr,tip_rou2);
		}
		else
		{
			SetMain(rebound_rou1,plyr,rebound_rou2);
		}

		/*
		 * save current frame and anim
		 */
		fr    = plyr->top_frame;
		anim  = plyr->top_anim;
		lanim = plyr->leg_anim;
		lfr   = plyr->leg_frame;

		/*
		 * get ball pos at start of next anim
		 */
		plyr->top_anim = next_top_anim;
		plyr->top_frame = 0;
		plyr->top_blend_ctr=0;

		plyr->leg_anim = LEG_REB_AIR;
		plyr->leg_frame = 0x800;
		PlayGfxEvalBall(plyr, &x_diff, &z_diff, &y_diff);

		/*
		 * restore current frame and anim
		 */
		plyr->leg_anim  = lanim;
		plyr->leg_frame = lfr;
		plyr->top_anim  = anim;
		plyr->top_frame = fr;

		time = plyr->ctr2 - lp_ctr;
		if (time == 0)
		{
			BRK;
			time=1;
		}

		//set up velocities
		x_diff	   = ball_cos[(plyr->ctr2-proj_time)*3] - x_diff;
		plyr->xvel = x_diff / time;

		y_diff	   = ball_cos[(plyr->ctr2-proj_time)*3 +1] - y_diff;
		plyr->yvel = y_diff / time;

		z_diff	   = ball_cos[(plyr->ctr2-proj_time)*3 +2] - z_diff;
 		plyr->zvel = (z_diff/time) + ((time-1)*(LITTLE_G/2));

		start_leg_anim2(plyr, LEG_REB_AIR, time_till_land(plyr));
	}
}



void tip_rou2(playert *plyr)
{
int land_time;
int anim_time;

	CallChainP(tip_rou2);

	plyr->zco += plyr->zvel;
	if (plyr->zco < 0)
	{
		plyr->zco = 0;
		plyr->zvel = 0;
	}

	plyr->zvel -= LITTLE_G;
   	plyr->xco += plyr->xvel;
   	plyr->yco += plyr->yvel;

	if (end_animation_test(plyr))
	{
		//if ((rebound_flag==0) && (!possession_flag))	BRK;

		if ((!possession_flag) && (rebound_flag) && (in_play_flag))
		{
			start_next_anim(plyr);

			check_rebound(plyr);

			get_new_controls(&ai_team[plyr->team], plyr);
			catch_ball(plyr);
			SetMain(tip_rou2,plyr,tip_shoot);
		}
		else
		{
			start_anim(plyr, TOP_TIP_DN);
			SetMain(tip_rou2,plyr,rebound_rou3);

			land_time = time_till_land(plyr);
			anim_time = frames_to_go(plyr)-3;

			if (anim_time < land_time)
			{
				plyr->top_rate = (anim_time<<8)/land_time;
			}
		}
	}
}


void tip_shoot(playert *plyr)
{
	CallChainP(tip_shoot);

	if (possession_flag)
		follow_player(&ball);

	plyr->zco += plyr->zvel;
	if (plyr->zco < 0)
	{
		plyr->zco = 0;
		plyr->zvel = 0;
	}

	plyr->zvel -= LITTLE_G;
   	plyr->xco += plyr->xvel;
   	plyr->yco += plyr->yvel;

	if (end_animation_test(plyr))
	{
		SetDunkFlag(tip_shoot,FALSE);
		pt3_flag	= FALSE;

		if (possession_flag)
			tip_release(plyr);

		ball_spinx = 0x1000;
		ball_spiny = 0;
		ball_spinz = 0;

		start_next_anim(plyr);
		SetMain(tip_shoot,plyr,rebound_rou3);

		Comment( COMMENT_TIP_IN, plyr, 0 );
	}
}



void rebound_rou2(playert *plyr)
{
int land_time;
int anim_time;

	CallChainP(rebound_rou2);

	plyr->zco += plyr->zvel;
	if (plyr->zco < 0)
	{
		plyr->zco = 0;
		plyr->zvel = 0;
	}
	plyr->zvel -= LITTLE_G;

   	plyr->xco += plyr->xvel;
   	plyr->yco += plyr->yvel;

	if (end_animation_test(plyr))
	{
		if ( (!possession_flag) && (rebound_flag) && ((plyr->control) || (!human_reb_flag))
			&& (in_play_flag))
		{
			start_next_anim(plyr);

			//update stats and reset play
			check_rebound(plyr);

			get_new_controls(&ai_team[plyr->team], plyr);
			catch_ball(plyr);
			plyr->xvel/=2;
			plyr->yvel/=2;
//old			AudioPlaySfx(SFX_ID_CATCH,22050,CATCH_VOL,7);
			Audio_SfxPlay( GameBank, SFX_CATCH3, SfxVolume );

			SetMain(rebound_rou2,plyr,rebound_rou3);
		}
		else
		{
			start_anim(plyr, TOP_REB_MISS);
			SetMain(rebound_rou2,plyr,rebound_rou3);
		}

		land_time = time_till_land(plyr)+1;
		anim_time = frames_to_go(plyr)-3;

		if (anim_time < land_time)
		{
			plyr->top_rate = (anim_time<<8)/land_time;
		}
	}
}




void rebound_rou3(playert *plyr)
{
	CallChainP(rebound_rou3);

	plyr->zco += plyr->zvel;
	if (plyr->zco <= 1)
	{
		//stop being pushed whilst landing sequence is on
		plyr->zco = 1;
		plyr->zvel = 0;

		start_leg_anim2(plyr, LEG_REB_LD, 10);
		SetMain(rebound_rou3,plyr,rebound_landed);
	}
	else
	{
		plyr->zvel -= LITTLE_G;

		if (!possession_flag)
		{
			//collision off
	   		plyr->xco  += plyr->xvel;
   			plyr->yco  += plyr->yvel;
		}
		else
		{
			int di;

			//separation increases at 5cm/tick after catching ball
			di = possession_ticks*0x8000;
			if (di>COL_DI)		di = COL_DI;
			collision(plyr, plyr->xvel, plyr->xvel, di);
		}
	}
}




void rebound_landed(playert *plyr)
{
	CallChainP(rebound_landed);

	if (end_animation_test(plyr))
		if (end_leg_animation_test(plyr))
		{
			if ((!possession_flag) || (ball_handler != plyr->no))
			{
				SetMain(rebound_landed,plyr,deflt);
				plyr->top_routine  = top_deflt;
			}
			else
			{
				SetMain(rebound_landed,plyr,stand_with_ball);
				plyr->top_routine  = NULL;
			}

			plyr->leg_routine  = leg_deflt;
			plyr->zco = 0;
		}
}
