//*****************************************************************************
//	Include Dependencies
//*****************************************************************************
#include "generic.h"
#include "lfptypes.h"
#include "define.h"
#include "ml.h"

#include "playgfx.h"
#include "crowd.h"
#include "comment.h"

#include "gamefx.h"
#include "playbyplay.h"

extern u8
	*GameBank;


//*****************************************************************************
//	Defines
//*****************************************************************************
#define	BLOCK_DZ	0x90000			// Maximum z differential to block ball
#define	BLOCK_DZP	0x60000			// Maximum positive z differential to block
#define	BLOCK_RATE	0x4000			// Blocking rate (0xffff is 100%)
#define	BLOCK_MIN_BZ	0x238000	// Minimum elevation of ball to block it

//#define	REPLAY_DELAY	45			// # of ticks til action replay after block
#define	REPLAY_DELAY	0			// Turns off block replays

#define GOALTENDING_RAD 0x60000
#define GOALTENDING_Z   BASKET_Z

//*****************************************************************************
//	Local Routine Declarations
//*****************************************************************************
static bool block_dunk_test2(playert *plyr);
static bool block_jump_test(playert *plyr);
static bool	handle_block(playert *plyr);


void IncShotAttempts(playert *plyr);	// in shoot.c


//*****************************************************************************
//	Routines
//*****************************************************************************

playert *look_for_blocker(void)
{
playert *plyr;

	for (plyr = cur_team->plyr1; plyr < cur_team->plyr1+5; plyr++)
		if (plyr->control == 0)	//Can't take control of human players
			if (plyr->main_routine == deflt)
				if (plyr->ball_dist<0x300000)
					if (plyr->basket_dist<0x400000)
						block_dunk_test(plyr);

	return NULL;
}


bool hand_hits_ball(playert *plyr)
{
	int	idx;
	int	x,y,z;
	int	dx,dy,d;
	int	bx,by,bz;

#if 1
	project(bhp,1,FALSE);
	if (PlayGfxEvalBall(&DUMMY,&bx,&bz,&by))
#endif
	{
		bx=ball.xco;
		by=ball.yco;
	}

	if (switch_ends_flag)
	{
		bx=-bx;
		by=-by;
	}

	idx=(plyr->top_anim>0)? NODE_RHAND : NODE_LHAND;

#if 0
	project(plyr,1,FALSE);
	x=DUMMY.nodeVector[idx].x<<10;
	y=DUMMY.nodeVector[idx].z<<10;
#else
	x=plyr->nodeVector[idx].x<<10;
	y=plyr->nodeVector[idx].z<<10;
#endif

	idx=(plyr->top_anim>0)? NODE_RHAND : NODE_LHAND;

	dx=x-bx;
	dy=y-by;

	d=MANHATTAN(dx,dy);

	if (d<0x50000)
		return TRUE;
	else
		return FALSE;
}


bool block_dunk_test(playert *plyr)
{
int x_diff;
int y_diff;
int z_diff;

int xco, yco, zco;

int z_vel;

int time, t;
int start_time, end_time;

int h_dist;
int next_anim;

bool flag = FALSE;
SW		da;

	if (!dunk_flag)			return FALSE;

	if (!in_play_flag)		return FALSE;

	if (!possession_flag)	return FALSE;

	if ((bhp->scale-plyr->scale)*78>(7<<8))
		return FALSE;

	// When a fast break is not in progress, do not allow blocking from behind
	if (!fast_break_flag)
	{
		da=LFPAtn(plyr->xco-bhp->xco,plyr->yco-bhp->yco)-bhp->angle;
		if (abs(da)>0x5000)
			return FALSE;
	}

	xco = plyr->xco;
	yco = plyr->yco;
	zco = (plyr->scale * 9551);				//2.5m from ground

	start_time = lp_ctr - proj_time + BLOCK_TIME+5;	//5 ticks to get off ground, 5 ticks
	end_time   = start_time+20;

	if (end_time >= (end_proj_time-proj_time))		end_time = end_proj_time-proj_time-1;

	for (time = start_time+2; time<end_time; time += 1)
	{
		if (ball_cos[time*3 +2] < 0x400000)
		{
			//no blocks on the ground please
			if (ball_cos[time*3+2]<BLOCK_MIN_BZ)
				continue;

			z_diff = ball_cos[time*3 +2] - zco;
			if (z_diff > 0)		//ball is above hands
			{
				//get t, time for actual jump
				t = time-lp_ctr+proj_time-BLOCK_TIME;

				if (t<3)
					continue;

				z_vel  = (z_diff/t)	+ ((t-1)*(LITTLE_G/2));

				if (z_vel - ((t + 2)*LITTLE_G) < 0)		//grab ball within 2 ticks of peak
				{
			   //	if (z_vel < (LITTLE_G*ONE_SEC/2))
			   //	{

						x_diff = ball_cos[time*3] - xco;
						y_diff = ball_cos[time*3 +1] - yco;

						h_dist = MANHATTAN(x_diff, y_diff);

						if ( h_dist < (t*0x20000) && rand()<BLOCK_RATE )
						{
#ifdef DEBUG
							printf("lp1 %d, ct2 %d, time %d, ball %x,%x,%x\n",
								lp_ctr,
								time+proj_time,
								time,
								ball_cos[time*3],
								ball_cos[time*3 + 1],
								ball_cos[time*3 + 2]);
#endif
							plyr->angle2 = LFPAtn( x_diff, y_diff);
							next_anim = ((SW)(plyr->angle2-plyr->angle) > 0) ? -TOP_BLOCK1H1:TOP_BLOCK1H1;
							plyr->ctr2	 = time+proj_time;

							start_blend_anim2(plyr, next_anim, plyr->ctr2-lp_ctr);
							start_blend_leg_anim2(plyr, LEG_REB_TO, BLOCK_TIME);
							SetMain(block_dunk_test,plyr,block_rou1);
							plyr->leg_routine  = NULL;
							plyr->top_routine  = NULL;
							return TRUE;
						}
				  //}
				}
			}
		}
	}

	return FALSE;
}




void block_rou1(playert *plyr)
{
int time, t;
int	x_diff;
int	y_diff;
int	z_diff;
int anim, frame, lanim, lfr;
int bc;

	int	xv,yv;

	CallChainP(block_rou1);

	//if (!plyr->control)
		turn_to(plyr, (SW)plyr->angle2);	//face ball

	if (end_leg_animation_test(plyr))
	{
		xv=plyr->xvel;
		yv=plyr->yvel;

		SetMain(block_rou1,plyr,block_rou2);
		plyr->blk_release=10;

		/*
		 * save current frame and anim
		 */
		frame = plyr->top_frame;
		anim  = (plyr->top_anim>0) ? TOP_BLOCK1H1: -TOP_BLOCK1H1;
		lfr   = plyr->leg_frame;
		lanim = plyr->leg_anim;

		/*
		 * get ball pos at start of next anim
		 */
		plyr->top_anim  = (anim>0) ? anim+1: anim-1;
		plyr->top_frame = 0;
		plyr->leg_anim  = LEG_REB_AIR;
		plyr->leg_frame = 0x800;
		bc = plyr->top_blend_ctr;
		plyr->top_blend_ctr = 0;

		PlayGfxEvalBall(plyr, &x_diff, &z_diff, &y_diff);

		/*
		 * restore current frame and anim
		 */
		plyr->top_blend_ctr = bc;
		plyr->leg_frame = lfr;
		plyr->leg_anim  = lanim;
		plyr->top_frame = frame;
		plyr->top_anim  = anim;

		time = plyr->ctr2 - proj_time;
		t    = plyr->ctr2 - lp_ctr;

		if (t<3)
		{
			start_blend_anim(plyr, TOP_RDY);
			start_blend_leg_anim(plyr, LEG_RDY);
			SetMain(block_rou1,plyr,deflt);
			plyr->top_routine = top_deflt;
			plyr->leg_routine = leg_deflt;
		}

		//set frame rate to catch ball
		plyr->top_rate = ((((playerAnims[abs(anim)].a->numFrames-1)<<8) - plyr->top_frame) + t)/t;

		//set vels necessary to catch ball
		x_diff	   = ball_cos[time*3] - x_diff;
		plyr->xvel = x_diff/t;

		y_diff	   = ball_cos[time*3 +1] - y_diff;
		plyr->yvel = y_diff/t;

		z_diff	   = ball_cos[time*3 +2] - z_diff;
 		plyr->zvel = (z_diff/t) + ((t-1)*(LITTLE_G/2));

			if (plyr->zvel>MIN_MAX(plyr->plyrROM->ub___pAttrJump, 0x18000, 0x30000))
			{
				plyr->xvel=xv;
				plyr->yvel=yv;
				plyr->zvel=0;
				set_default(plyr);
				return;
			}

		if ((UI)(MANHATTAN(plyr->xvel, plyr->yvel) * (plyr->zvel/LITTLE_G)) > 0x300000)
		{
			//BRK;
		}

		start_leg_anim2(plyr, LEG_REB_AIR, time_till_land(plyr));
	}
}



void block_rou2(playert *plyr)
{
int land_time;
int anim_time;
int rad;

	CallChainP(block_rou2);

	//update x, y and z
	plyr->zco += plyr->zvel;
	plyr->zvel -= LITTLE_G;
	if (plyr->zco < 0)
	{
		plyr->zco = 0;
		plyr->zvel = 0;
	}

	plyr->xco+= plyr->xvel;
	plyr->yco+= plyr->yvel;

	if (plyr->control)
	{
		if ((current_ctrlr->ctrlr^current_ctrlr->last) & CTRL_SHOOT)
		{
			//not released B yet
			plyr->blk_release = 0;
		}
		else if ((current_ctrlr->ctrlr&CTRL_SHOOT) == 0)
		{
			//released B
			plyr->blk_release++;
		}
	}
	else
	{
		//CPU blocks
		plyr->blk_release=10;
	}



	if (end_animation_test(plyr))
	{
		start_next_anim(plyr);
		SetMain(block_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;
		}

		// If ballhandler has switched to passing, don't try to block it & cause a foul
		if (!(shoot_stage || dunk_flag || ball.main_routine==shot_ball_move))
			return;

		if (((possession_flag) || (ball.main_routine == shot_ball_move)) && (in_play_flag))
		{
			//stop going up
			if (plyr->zvel > 0)		plyr->zvel = 0;

			//don't go forward as fast
			plyr->xvel >>= 1;
			plyr->yvel >>= 1;

			if (possession_flag)
			{
				int nothing_rating;
				int block_rating;
				int foul_rating;
				int random;

				/* 
				 * generate scores for different outcomes
				 * do nothing
				 * call foul
				 * legitimate block
				 */
				if (dunk_flag)
				{
					int pd, bd;


					block_rating   = plyr->plyrROM->ub___pAttrBlock+50;

					if (plyr->control)
					{
						if (plyr->blk_release<MIN_MAX(plyr->plyrROM->ub___pAttrBlock, 2,6))
						{
							block_rating += 50;
						}
					}

					foul_rating    = (plyr->plyrROM->ub___pAttrFoul*25)+33;

					bd = MANHATTAN(ball.xco-plyr->xco, ball.yco-plyr->yco);
					pd = MANHATTAN(bhp->xco-plyr->xco, bhp->yco-plyr->yco);
					//stop /0
					if (bd==0)	bd=1;	
					//am I closer than guy with ball?
					if (pd<bd)	foul_rating += (100*(bd-pd))/bd;
					
					if (DEFENSIVE_FOULS>1)
					{
						nothing_rating = ((4-DEFENSIVE_FOULS) * foul_rating) / 3;
						foul_rating    = ((DEFENSIVE_FOULS-1) * foul_rating) / 3;
					}
					else
					{
						nothing_rating = foul_rating;
						foul_rating = 0;
					}
				}
				else
				{
					//BRK;

					/*
					block_rating   = plyr->plyrROM->ub___pAttrBlock;
					if (plyr->control)
					{
						if (plyr->blk_release<MIN_MAX(plyr->plyrROM->ub___pAttrBlock, 1,5))
						{
							block_rating += 50;
						}
					}

					foul_rating    = (plyr->plyrROM->ub___pAttrFoul*25)+100;

					switch(abs(bhp->top_anim))
					{
					case TOP_FJS2:
					case TOP_FJS3:
					case TOP_FJS4:
					case TOP_HOOK1:
					case TOP_HOOK2:
					case TOP_HOOK3:
					case TOP_BHOOK1:
					case TOP_BHOOK2:
					case TOP_BHOOK3:

						nothing_rating += block_rating;
					 	block_rating = 0;
						break;
					}

					nothing_rating = ((4-DEFENSIVE_FOULS)*foul_rating) / 4;
					foul_rating    = ((DEFENSIVE_FOULS) * foul_rating) / 4;
					*/

					//can't block non-dunk shots
					nothing_rating = 100;
					foul_rating    =
					block_rating   = 0;
				}

				if (!project_flag)
				{
					bool	hhb;

					block_rating+=nothing_rating;
					random = RANDOM(foul_rating+block_rating);

					hhb=hand_hits_ball(plyr);

					if (random < block_rating && hhb)
					{
						int x,y,z;

						//halt action for 2 ticks
						freeze = 2;

						//place ball at end players arm
						ball_place(plyr);

						SetPossession(block_rou2,FALSE);
						SetPickedUpFlag(block_rou2,FALSE);
						SetMain(block_rou2,&ball,loose_ball_move);

						if (!foul_flag2)
						{
							if ((ball.zco > GOALTENDING_Z) &&
								(MANHATTAN(ball.xco, ball.yco-current_basket_y) < GOALTENDING_RAD)
								&& (GOALTENDING) 	// fixes bug #1056
								)
							{
								SetDunkFlag(block_rou2,FALSE);	// Just in case
								goto goal_tending;
							}
							
							SetLastHandler(bloock_rou2,plyr->no);
							stats_block(plyr);
							AR_Time=REPLAY_DELAY;
							MOMUP(plyr->team,5);
						}

						Comment( COMMENT_BLOCK, plyr, 0 );

						//rumble PAK
						if (bhp->control)	CtrlMotorOn(bhp->control-1, 2);
						if (plyr->control)	CtrlMotorOn(plyr->control-1,2);

						//sound
//old						AudioPlaySfx(SFX_ID_BLOCK,22050,255,7);
						Audio_SfxPlay( GameBank, SFX_BLOKSMAK, SfxVolume );

						//spike ball away
						ball.xvel = plyr->xvel + LFPCos(plyr->angle);
						ball.yvel = plyr->yvel + LFPSin(plyr->angle);
						ball.zvel = plyr->zvel>>1;

						SetDunkFlag(block_rou2,FALSE);
						time_rate=0x100;
						if (CAMERAS==1)	CameraSpecialEnd();
					}
					else if ( /*random < (block_rating+foul_rating) &&*/ hhb && DEFENSIVE_FOULS)
					{
						if (!project_flag)
						{
							//rumble PAK
							if (bhp->control)	CtrlMotorOn(bhp->control-1, 2);
							if (plyr->control)	CtrlMotorOn(plyr->control-1,2);
						}

						if (plyr->basket_dist < bhp->basket_dist && !bhp->zco)
						{
							//knock bhp down
							FoulSet(block_rou2_knockdown,plyr, HACKING2);
							free_thrower = ball_handler;
							start_fall(plyr, (SW)bhp->basket_angle);
						}
						else
						{
							//allow bhp to continue
							FoulSet(block_rou2_continue,plyr, HACKING2);
							free_thrower = ball_handler;
						}

						if (CAMERAS==1)	CameraSpecialEnd();
					}
				}
			}
			else if (ball.main_routine==shot_ball_move && hand_hits_ball(plyr))
			{
				// Either way, no halfcourt violations possible
				SetHalfFlag(block_rou2,FALSE);

				//GOALTEND
				if (((ball.zvel < -(LITTLE_G*3)) || ball.no2) && (GOALTENDING) && (shot_at_time >= 0) )
				{
goal_tending:
					db_out(block_rou2,"Goaltending");
					if (ball.zvel<LITTLE_G*3)
						db_out(block_rou2,"Trajectory");

					//ref blows whistle
					Audio_SfxPlay( GameBank, SFX_WHISTLE, SfxVolume );
					Speech_Delay(20);
					Speech_PlayByPlay(SFX_GOALTENDING);

					SetHalfFlag(block_rou2,FALSE);
					stats_points(player+last_handler, pt3_flag? 3 : 2);
					scored_flag=(ball.yco<0)? 1 : -1;

					OverGfx_ShowScore(ONE_SEC*3/2,ONE_SEC*3);	// fixes bug #928a

					if (foul_flag2)
						return;

					time_flag = (time_left > ( (pcl__gMatch->sb___mQuarter>2) ? (2*ONE_MIN):ONE_MIN) );

					ball.yco = current_basket_y;
					ball.xco = 0;
					start_drop_bounce(&ball);

					ball.target_x = 0x100000;
					ball.target_y = (ball.yco>0) ? BASE_LINE+EDGE: -BASE_LINE-EDGE;
					scored_flag = (ball.yco>0) ? 1:-1;
					return;
				}
				else
				{
					//legitimate block
					db_out(block_rou2,"Block");

					//place ball at end of players arm
					ball_place(plyr);

					if ((ball.zco > GOALTENDING_Z) &&
						(MANHATTAN(ball.xco, ball.yco-current_basket_y) < GOALTENDING_RAD)
						&& (GOALTENDING)	// fixes bug #1056
						)
					{
						SetDunkFlag(block_rou2,FALSE);
						goto goal_tending;
					}

					SetLastHandler(bloock_rou2,plyr->no);
					SetHalfFlag(block_rou2,FALSE);
					MOMUP(plyr->team,5);

					SetPossession(block_rou2,FALSE);
					SetPickedUpFlag(block_rou2,FALSE);
					SetMain(block_rou2,&ball,loose_ball_move);

					AR_Time=REPLAY_DELAY;
					stats_block(plyr);

					Comment( COMMENT_BLOCK, plyr, 0 );

					if (plyr->control)	CtrlMotorOn(plyr->control-1,2);

//old					AudioPlaySfx(SFX_ID_BLOCK,22050,255,7);
					Audio_SfxPlay( GameBank, SFX_BLOKSMAK, SfxVolume );

					//spike ball
					ball.xvel = plyr->xvel + (LFPCos(plyr->angle)*8);
					ball.yvel = plyr->yvel + (LFPSin(plyr->angle)*8);
					ball.zvel = plyr->zvel>>1;

					SetDunkFlag(block_rou2,FALSE);
					time_rate=0x100;
				}

				if (CAMERAS==1)	CameraSpecialEnd();
			}
		}
	}
}


static void block_xy(playert *plyr)
{
int time, xvel, yvel;
int tx, ty;
SW  ba;

	if (shoot_stage)
	{
		time = projected_release_time - lp_ctr;

		ba = LFPAtn(-projected_release_x, current_basket_y-projected_release_y);
		
		tx = projected_release_x + (LFPCos(ba)<<5);
		ty = projected_release_y + (LFPSin(ba)<<5);

		if (time<2)	time = 2; 

		xvel = (tx - plyr->xco)/time;
		yvel = (ty - plyr->yco)/time;

		limit_acel(plyr, xvel, yvel, 0x10000);
	}
}


void block_start(playert *plyr)
{
	CallChainP(block_start);

	plyr->top_routine =
	plyr->leg_routine = NULL;

	// change block time to vary according to player's blocking ability
	if (MANHATTAN(plyr->xvel, plyr->yvel) > 0x18000)
	{
		SW a_diff;

		//which arm is nearest ball

		a_diff = LFPAtn(-projected_release_x ,current_basket_y - projected_release_y)
				-LFPAtn(-plyr->xco           ,current_basket_y - plyr->yco);
		start_blend_anim2(plyr, (a_diff>0) ? -TOP_RBLOCK1:TOP_RBLOCK1, BLOCK_TIME);
		start_blend_leg_anim2(plyr, LEG_RBLK1, BLOCK_TIME);

		SetMain(block_start,plyr,run_block1);

		plyr->xvel >>= 1;
		plyr->yvel >>= 1;
	}
	else
	{
		int time;
		int xvel, yvel;

		start_blend_anim(plyr, TOP_BLOCK1H1);
		start_leg_anim2(plyr, LEG_REB_TO, BLOCK_TIME);


		time = (J_ZVEL/LITTLE_G) + BLOCK_TIME;

		if (DEFENDING)
		{
			switch(abs(plyr->last_top_anim))
			{

			case TOP_LOW_TO_HI:
				// if arms far enough up treat as gaurding high...
				// ...else normal
				if (plyr->last_top_frame < 0x300)	break;

			case TOP_GRDH:
				// two handed block
				plyr->top_anim = TOP_BLOCK2H1;

			case TOP_LP_GRD:

				if (dunk_flag)
				{
					if (block_dunk_test2(plyr))	return;
				}
				else
				{
					if (block_jump_test(plyr))	return;
				}

				plyr->top_frame = 0x1400;
				plyr->zvel = B_ZVEL;
				start_leg_anim2(plyr, LEG_REB_AIR, time_till_land(plyr));
				SetMain(block_start,plyr,jump_block_rou2);
				plyr->top_rate     = (frames_to_go(plyr)<<8) / (time-BLOCK_TIME);

				block_xy(plyr);

				return;
			}
		}

		SetMain(block_start,plyr,jump_block_rou1);
		plyr->top_rate     = (frames_to_go(plyr)<<8) / time;
	}
}


void run_block1(playert *plyr)
{
	CallChainP(run_block1);

	collision(plyr, plyr->xvel, plyr->yvel, COL_DI);

	if (end_animation_test(plyr))
	{
		plyr->zvel = B_ZVEL;
		start_next_anim2(plyr, time_till_land(plyr));
		start_next_leg_anim2(plyr, time_till_land(plyr));
		SetMain(run_block1,plyr,run_block2);

		if (!intercept_jump_test(plyr))		block_xy(plyr);
	}
}


void run_block2(playert *plyr)
{
	int dx,dy;		// Delta along axes between ball and player
	int	dist;		// Distance between the ball and player

	CallChainP(run_block2);

	// Update player positioning
	collision(plyr, plyr->xvel, plyr->yvel, COL_DI);

	plyr->zco  += plyr->zvel;
	plyr->zvel -= LITTLE_G;

	// Check for landing
	if (plyr->zco <= 0)
	{
		start_next_anim(plyr);
		start_next_leg_anim(plyr);
		SetMain(run_block2,plyr,run_block3);
		start_vels(plyr, 32, (SW)plyr->angle);
		return;
	}

	// Am I reaching out & is ball going up and in play?
	//if (!possession_flag)
		if (handle_block(plyr))
		{
			start_next_anim2(plyr, time_till_land(plyr)+5);
			SetMain(run_block2,plyr,jump_block_rou3);
			return;
		}
}


void run_block3(playert *plyr)
{
	CallChainP(run_block3);

	collision(plyr, plyr->xvel, plyr->yvel, COL_DI);

	if (blend_to_end(plyr, TOP_RDY, LEG_RDY, 2))
	{
		SetMain(run_block3,plyr,deflt);
		plyr->top_routine  = top_deflt;
		plyr->leg_routine  = leg_deflt;
	}
}





void jump_block_rou1(playert *plyr)
{
	CallChainP(jump_block_rou1);

	//if (!plyr->control)
		turn_to(plyr, (SW)plyr->ball_angle);	//face ball

	if (end_leg_animation_test(plyr))
	{
 		if (block_jump_test(plyr))	return;

		plyr->zvel = B_ZVEL;
		start_leg_anim2(plyr, LEG_REB_AIR, time_till_land(plyr));
		SetMain(jump_block_rou1,plyr,jump_block_rou2);

		if (!intercept_jump_test(plyr))		block_xy(plyr);
	}
}


void jump_block_rou2(playert *plyr)
{
int dist;
int land_time;

	CallChainP(jump_block_rou2);

	//update z
	plyr->zco  += plyr->zvel;
	plyr->zvel -= LITTLE_G;
	if (plyr->zco < 0)
	{
		start_leg_anim2(plyr, LEG_REB_LD,5);
		SetMain(jump_block_rou2,plyr,rebound_landed);
		plyr->zco = 0;
	}

	//update x,y
	collision(plyr, plyr->xvel, plyr->yvel, COL_DI);

	
	land_time = time_till_land(plyr);
	if (land_time < 4)
	{
	 	start_next_anim2(plyr, land_time+5);
		SetMain(jump_block_rou2,plyr,jump_block_rou3);
		return;
	}


	// New collision check based upon the cylinder-sphere test
	//if (!possession_flag)
		if (handle_block(plyr))
		{
			start_next_anim2(plyr, time_till_land(plyr)+5);
			SetMain(jump_block_rou2,plyr,jump_block_rou3);
			return;
		}

	if (end_animation_test(plyr))
	{
		start_next_anim2(plyr, time_till_land(plyr)+5);
		SetMain(jump_block_rou2,plyr,jump_block_rou3);
	}

}




void jump_block_rou3(playert *plyr)
{
	CallChainP(jump_block_rou3);

	//update z
	plyr->zco  += plyr->zvel;
	plyr->zvel -= LITTLE_G;

	//update x and y
	collision(plyr, plyr->xvel, plyr->yvel, COL_DI);

	if (!possession_flag)
	{
		handle_block(plyr);
		SetMain(jump_block_rou3,plyr,blocked_rou3);
		return;
	}

	if (plyr->zco < 0)
	{
		//landed
		start_leg_anim2(plyr, LEG_REB_LD,5);
		SetMain(jump_block_rou3,plyr,rebound_landed);
		plyr->zco = 0;
	}
}


void blocked_rou2(playert *plyr)
{
	CallChainP(blocked_rou2);

	//update z
	plyr->zco  += plyr->zvel;
	plyr->zvel -= LITTLE_G;

	//update x and y
	collision(plyr, plyr->xvel, plyr->yvel, COL_DI);

	if (end_animation_test(plyr))
	{
		start_next_anim2(plyr, time_till_land(plyr)+5);
		SetMain(blocked_rou2,plyr,blocked_rou3);
	}
}

void blocked_rou3(playert *plyr)
{
	CallChainP(blocked_rou3);

	//update z
	plyr->zco  += plyr->zvel;
	plyr->zvel -= LITTLE_G;

	//update x and y
	collision(plyr, plyr->xvel, plyr->yvel, COL_DI);

	if (plyr->zco < 0)
	{
		//landed
		start_leg_anim2(plyr, LEG_REB_LD,5);
		SetMain(blocked_rou3,plyr,rebound_landed);
		plyr->zco = 0;
	}
}



static bool block_dunk_test2(playert *plyr)
{
int x_diff;
int y_diff;
int z_diff;

int xco, yco, zco;

int z_vel;

int time, t;
int start_time, end_time;

int h_dist;
int next_anim;

bool flag = FALSE;


	if (!dunk_flag)			return FALSE;
	if (!in_play_flag)		return FALSE;
	if (!possession_flag)	return FALSE;

	xco = plyr->xco;
	yco = plyr->yco;
	zco = (plyr->scale * 9551);				//2.5m from ground

	start_time = lp_ctr - proj_time + 3;
	end_time   = start_time + 20;

	if (end_time >= (end_proj_time-proj_time))		end_time = end_proj_time-proj_time-1;

	for (time = start_time+2; time<end_time; time += 1)
	{
		if (ball_cos[time*3 +2] < 0x400000)
		{
			z_diff = ball_cos[time*3 +2] - zco;
			if (z_diff > 0)
			{

				t = time + proj_time - lp_ctr;

				z_vel  = (z_diff/t)
							+ ((t - 1)*(LITTLE_G/2));

				if (z_vel - ((t+2)*LITTLE_G) < 0)		//grab ball within 2 ticks of peak
				{
					x_diff = ball_cos[time*3] - xco;
					y_diff = ball_cos[time*3+1] - yco;

					h_dist = MANHATTAN(x_diff, y_diff);

					if ( h_dist < (t * 0x40000) )
					{
						plyr->angle2 = LFPAtn( x_diff, y_diff);
						next_anim = ((SW)(plyr->angle2-plyr->angle) > 0) ? -TOP_BLOCK1H1:TOP_BLOCK1H1;
						plyr->ctr2 = time+proj_time;

						block_rou1(plyr);
						return TRUE;
					}
				}
			}
		}
	}

	return FALSE;
}



static bool block_jump_test(playert *plyr)
{
int x_diff;
int y_diff;
int z_diff;

int xco, yco, zco;

int z_vel;

int time, t;
int start_time, end_time;

int h_dist;
int jump_z;

bool flag = FALSE;
SW		da;


	if (!in_play_flag)			return FALSE;

	if (!possession_flag)		return FALSE;

	da=LFPAtn(plyr->xco-bhp->xco,plyr->yco-bhp->yco)-bhp->angle;
	if (abs(da)>0x5000)
		return FALSE;

	xco = plyr->xco;
	yco = plyr->yco;
	zco = (plyr->scale * 9551);				//2.5m from ground

	start_time = lp_ctr-proj_time+3;
	end_time   = start_time+(bhp->zvel/LITTLE_G);

	if (end_time >= (end_proj_time-proj_time))		end_time = end_proj_time-proj_time-1;

	if (end_time<start_time)	return FALSE;

	jump_z = MIN_MAX(plyr->plyrROM->ub___pAttrJump, 0x18000, 0x30000);

	for (time = start_time; time<=end_time; time += 1)
	{
		// Do not block a ball that has already hit the glass
		if (ball_glass_time>=0)
			if (time>=ball_glass_time)
				return FALSE;

		if (ball_cos[time*3 +2] < 0x400000)
		{
			if (ball.zco<BLOCK_MIN_BZ)
				continue;

			z_diff = ball_cos[time*3 +2] - zco;

			//ball is above hands
			if (z_diff > 0)
			{
				t = time + proj_time - lp_ctr;

				z_vel  = (z_diff/t)
							+ ((t - 1)*(LITTLE_G/2));

				if (z_vel <= jump_z) 

//				if (z_vel - ((t+2)*LITTLE_G) < 0)		//grab ball within 2 ticks of peak
				{
					x_diff = ball_cos[time*3] - xco;
					y_diff = ball_cos[time*3+1] - yco;

					h_dist = MANHATTAN(x_diff, y_diff);

					if ( h_dist < (t * 0x40000) )
					{
						plyr->angle2 = LFPAtn( x_diff, y_diff);

						plyr->ctr2 = time+proj_time;

						SetMain(block_jump_test,plyr,block_rou1);
						block_rou1(plyr);
						return TRUE;
					}
				}
			}
		}
	}

	return FALSE;
}

bool handle_block(playert *plyr)
{
	int		sn;				// Shoulder node index
	int		hn;				// Hand node index
	float	sx,sy,sz;		// Shoulder position
	float	hx,hy,hz;		// Hand position
	float	bx,by,bz;		// Ball position
	float	ar;				// Arm radius
	float	br;				// Ball radius
	float	base=0x10000;


	// Check for non-blocking conditions
	if (ArcadeMode && !plyr->control)	// CPU can't block in arcade mode
		return FALSE;
	if (!in_play_flag)	// Don't block a ball that has gone through the hoop
		return FALSE;
	if (bhp->fouler && IS_DUNKING(bhp))	// Don't block a shooter who has alread been fouled
		return FALSE;

	//ar=(float)0x20000/base;
	ar=(ArcadeMode? 0x36000 : 0x20000)>>10;
	br=BALL_RADIUS>>10;

	if (plyr->top_anim>0)
	{
		sn=NODE_RSHOULDER;
		hn=NODE_RHAND;
	}
	else
	{
		sn=NODE_LSHOULDER;
		hn=NODE_LHAND;
	}

	sx=plyr->nodeVector[sn].x;
	sy=plyr->nodeVector[sn].y;
	sz=plyr->nodeVector[sn].z;

	hx=plyr->nodeVector[hn].x;
	hy=plyr->nodeVector[hn].y;
	hz=plyr->nodeVector[hn].z;

	bx=ball.flip_xco>>10;
	bz=ball.flip_yco>>10;
	by=ball.flip_zco>>10;

#if defined(DEBUG)
	PlotLine((int)sx<<10,(int)sz<<10,(int)sy<<10,(int)hx<<10,(int)hz<<10,(int)hy<<10,0,0xff,0xff);
	PlotLine((int)hx<<10,(int)hz<<10,(int)hy<<10,(int)bx<<10,(int)bz<<10,(int)by<<10,0,0xff,0);
#endif

	if (possession_flag && !bhp->zco)
		return FALSE;

	if (ball.main_routine==loose_ball_move && last_handler==plyr->no)	// Already blocked the ball
		return FALSE;

	//  If collision occurs, handle the results
	if (collide_sphere_cylinder(sx,sy,sz,hx,hy,hz,bx,by,bz,ar,br))
	{
		SW	pa;		// Player angle
		int	bs;		// Ball speed

		// Set up free ball stuff
		pa=plyr->angle;
		if (plyr->shuffle)
			ball.angle+=plyr->shuffle_angle>>12;

		if (ball.main_routine!=drop_bounce)
		{
			ball.angle=(3*pa-ball.angle)>>2;
			ball.zvel=-ball.zvel+(2*ReadRnd250()*(ball.zvel>>8));

			ball.ctr2=0;
			SetMain(handle_block,&ball,loose_ball_move);
			bs=pythag(ball.xvel,ball.yvel)*3/4;
			ball.xvel=(LFPCos(ball.angle)>>6)*(bs>>8);
			ball.yvel=(LFPSin(ball.angle)>>6)*(bs>>8);
		}

		rebound_flag=FALSE;
		SetHalfFlag(handle_block,FALSE);
		SetPossession(handle_block,FALSE);
		SetDunkFlag(handle_block,FALSE);
		if (bhp->fouler && ball.main_routine==follow_player)	// Blocking fouled shooter, correct stats
		{
			db_out(handle_block,"IncShotAttempts");
			IncShotAttempts(bhp);
		}
		SetPickedUpFlag(handle_block,FALSE);
		loose_flag=TRUE;

		end_proj_time=lp_ctr;
		project_ball(ONE_SEC);
		if (!project_flag)
		{
			time_rate=0x100;
			stats_block(plyr);
			pick_a_chaser();
			set_fast_break();

			if (!foul_flag2)
			{
				AR_Time=REPLAY_DELAY;
				SetLastHandler(handle_block,plyr->no);
			}
			Comment( COMMENT_BLOCK, plyr, 0 );
			MOMUP(plyr->team,5);
		}

		return TRUE;
	}

	return FALSE;
}
