/*********************************************************************************************/
/*                                                                                           */
/* Collision Routines                                                                        */
/*                                                                                           */
/*********************************************************************************************/

#include "game.h"

extern	char	*quadrant_names[];

	sint32		xfront,zfront;
/**********************************************************************************************
 *
 *									WOW !!!!
 *
 *		Get information about the specified position allowing for the facing of the object,
 *		its radius, and its height. Returns filled out INFO_STRUCT
 *********************************************************************************************/
void	GetCollisionInfo( COLL_INFO *coll, sint32 xpos, sint32 ypos, sint32 zpos,
					 	  sint16 room_number, sint32 objheight )
{
	sint32 		height,ceiling;
	sint32		xleft,zleft;
	sint32		xright,zright;
	sint32		x,y,z,ytop;
	FLOOR_INFO 	*floor;
	sint16		tilt;
	long			fheight,mheight,lheight,rheight;

	coll->coll_type = 0;
	coll->shift.x = coll->shift.y = coll->shift.z=0;
	coll->quadrant = (uint16)(coll->facing+0x2000)/0x4000;
	x = xpos;
	y = ypos - objheight;
	ytop = y - 160;
	z = zpos;

	floor = GetFloor( x,ytop,z, &room_number );				// Get Heights for MID Position
	mheight = height = GetHeight( floor,x,ytop,z );
	if ( height!=NO_HEIGHT )
		height -= ypos;
	ceiling = GetCeiling( floor,x,ytop,z );
	if ( ceiling!=NO_HEIGHT )
		ceiling -= y;

	coll->mid_floor = height;
	coll->mid_ceiling = ceiling;
	coll->mid_type = height_type;
	coll->trigger = trigger_index;
//	tilt = GetTiltType( floor,x,ytop,z );
   /* GAV: dodgy - used 'lara_item->pos.y_pos' on call to GetTiltType to stop Lara sliding
                   on slopes she's not standing on (i.e. when she's standing on the bridge). Whether
                   this screws up other aspects of Lara's movement I don't know, but seems to work OK */
   	tilt = GetTiltType( floor,x,lara_item->pos.y_pos,z );
	coll->tilt_z = tilt>>8;
	coll->tilt_x = (signed char)tilt;

//	coll->radius>>=1;
	switch ( coll->quadrant )								// Calculate X,Z offsets for
	{                                                       // Front and Left,Right sides
		case 0:
			xfront = (phd_sin(coll->facing)*coll->radius)>>W2V_SHIFT;
			zfront = coll->radius;
			xleft = -(coll->radius);
			zleft = coll->radius;
			xright = coll->radius;
			zright = coll->radius;
		break;

		case 1:
			xfront = coll->radius;
			zfront = (phd_cos(coll->facing)*coll->radius)>>W2V_SHIFT;
			xleft = coll->radius;
			zleft = coll->radius;
			xright = coll->radius;
			zright = -(coll->radius);
		break;

		case 2:
			xfront = (phd_sin(coll->facing)*coll->radius)>>W2V_SHIFT;
			zfront = -coll->radius;
			xleft = coll->radius;
			zleft = -(coll->radius);
			xright = -(coll->radius);
			zright = -(coll->radius);
		break;

		case 3:
			xfront = -(coll->radius);
			zfront = (phd_cos(coll->facing)*coll->radius)>>W2V_SHIFT;
			xleft = -(coll->radius);
			zleft = -(coll->radius);
			xright = -(coll->radius);
			zright = coll->radius;
		break;

		default:
			xleft = zleft = 0;
			xright = zright = 0;
			xfront = zfront = 0;
		break;
	}
//	coll->radius<<=1;

	//	Get Heights for Front position
	x = xpos + xfront;
	z = zpos + zfront;
	floor=GetFloor( x, ytop, z, &room_number);
	fheight = height = GetHeight( floor, x, ytop, z );
//	if (input & IN_ACTION)
//		printf("FH = %d MH = %d FRoom = %d\n",fheight,mheight,room_number);
	if ( height != NO_HEIGHT)
		height -= ypos;
	ceiling = GetCeiling( floor,x,ytop,z );
	if ( ceiling != NO_HEIGHT )
		ceiling -= y;
	coll->front_floor = height;

	coll->front_ceiling = ceiling;
	coll->front_type = height_type;

	if ( coll->slopes_are_walls==1 && (coll->front_type==BIG_SLOPE || coll->front_type == DIAGONAL) && coll->front_floor < 0 )
		coll->front_floor = -32767;
//	else if ( coll->slopes_are_walls==2 && (coll->front_type==BIG_SLOPE || coll->front_type == DIAGONAL) && (fheight < mheight))
//		coll->front_floor = -32767;
	else if ( coll->slopes_are_pits && (coll->front_type==BIG_SLOPE || coll->front_type == DIAGONAL) && (coll->front_floor > coll->mid_floor))
		coll->front_floor = 512;
	else if ( coll->lava_is_pit && coll->front_floor>0 && trigger_index && (*(trigger_index)&DATA_TYPE)==LAVA_TYPE )
		coll->front_floor = 512;

	// Get Heights for Left position
	x = xpos + xleft;
	z = zpos + zleft;
	floor=GetFloor( x, ytop, z, &room_number);
	lheight = height = GetHeight( floor, x, ytop, z );
	if ( height != NO_HEIGHT)
		height -= ypos;
	ceiling = GetCeiling( floor,x,ytop,z );
	if ( ceiling != NO_HEIGHT )
		ceiling -= y;
	coll->left_floor = height;
	coll->left_ceiling = ceiling;
	coll->left_type = height_type;
	if ( coll->slopes_are_walls==1 && (coll->left_type==BIG_SLOPE || coll->left_type == DIAGONAL) && coll->left_floor<0 )
		coll->left_floor = -32767;
//	else if ( coll->slopes_are_walls==2 && (coll->left_type==BIG_SLOPE || coll->left_type == DIAGONAL) && (lheight < mheight))
//		coll->left_floor = -32767;
	else if ( coll->slopes_are_pits && (coll->left_type==BIG_SLOPE || coll->left_type == DIAGONAL) && coll->left_floor>0 )
		coll->left_floor = 512;
	else if ( coll->lava_is_pit && coll->left_floor>0 && trigger_index && (*(trigger_index)&DATA_TYPE)==LAVA_TYPE )
		coll->left_floor = 512;

	// Get Heights for Right position
	x = xpos + xright;
	z = zpos + zright;
	floor=GetFloor( x, ytop, z, &room_number);
	rheight = height = GetHeight( floor, x, ytop, z );
	if ( height != NO_HEIGHT)
		height -= ypos;
	ceiling = GetCeiling( floor, x, ytop, z );
	if ( ceiling != NO_HEIGHT )
		ceiling -= y;
	coll->right_floor = height;
	coll->right_ceiling = ceiling;
	coll->right_type = height_type;
	if ( coll->slopes_are_walls==1 && (coll->right_type==BIG_SLOPE || coll->right_type == DIAGONAL) && coll->right_floor<0 )
		coll->right_floor = -32767;
//	if ( coll->slopes_are_walls==2 && (coll->right_type==BIG_SLOPE || coll->right_type == DIAGONAL) && (rheight < mheight))
//		coll->right_floor = -32767;
	else if ( coll->slopes_are_pits && (coll->right_type==BIG_SLOPE || coll->right_type == DIAGONAL) && coll->right_floor>0 )
		coll->right_floor = 512;
	else if ( coll->lava_is_pit && coll->right_floor>0 && trigger_index && (*(trigger_index)&DATA_TYPE)==LAVA_TYPE )
		coll->right_floor = 512;


	/* Gav 2/6/97: extra checks to stop Lara being launched inside wall when StaticObject collision takes place */
	if (CollideStaticObjects( coll,xpos, ypos, zpos,room_number, objheight ))
	{
		x = xpos + coll->shift.x;
		z = zpos + coll->shift.z;
		floor = GetFloor(x, ypos, z, &room_number);
		if (GetHeight(floor, x, ypos, z) < ypos - (WALL_L/2))
		{
			/* If there is a problem with the shift position, then reverse it; so will still fail if object fills
				a gap between two opposite walls and Lara drops on it, but this doesn't happen right? */
			coll->shift.x = -coll->shift.x;
			coll->shift.z = -coll->shift.z;
			// and coll->type reversed too?
		}
		else if (GetCeiling(floor, x, ypos, z) > ypos - objheight)
		{
			coll->shift.x = -coll->shift.x;
			coll->shift.z = -coll->shift.z;
		}
	}


//   Now collide with Walls and stuff...
	if ( coll->mid_floor==NO_HEIGHT )                  		// Shit No floor height at MidPoint..
	{
		coll->shift.x = coll->old.x - xpos;
		coll->shift.y = coll->old.y - ypos;
		coll->shift.z = coll->old.z - zpos;
		coll->coll_type = COLL_FRONT;
		return;
	}

	if ( coll->mid_floor-coll->mid_ceiling<=0 )
	{
		coll->shift.x = coll->old.x - xpos;					// Floor and Ceiling
		coll->shift.y = coll->old.y - ypos;                 // have caused a PINCH
		coll->shift.z = coll->old.z - zpos;
		coll->coll_type = COLL_CLAMP;
		return;
	}

	if ( coll->mid_ceiling>=0 )
	{
		coll->shift.y = coll->mid_ceiling;					// if only hit top then
		coll->coll_type = COLL_TOP;                         // shift downwards but still need to do front
	}														// and side tests so dont return now

	if ( coll->front_floor > coll->bad_pos ||
		 coll->front_floor < coll->bad_neg ||
		 coll->front_ceiling > coll->bad_ceiling )
	{
		if (coll->front_type == DIAGONAL || coll->front_type == SPLIT_TRI)
		{
			coll->shift.x = coll->old.x - xpos;            			// Leave X as old value
			coll->shift.z = coll->old.z - zpos;            			// Leave Z as old value
		}
		else if (coll->front_type == BIG_SLOPE)
		{
			switch ( coll->quadrant )
			{
				case 0:
				case 2:
					coll->shift.x = coll->old.x - xpos;            			// Leave X as old value
					coll->shift.z = FindGridShift2(zpos+zfront, zpos);		// Push Z
				break;

				case 1:
				case 3:
					coll->shift.x = FindGridShift2(xpos+xfront, xpos);		// Push X
					coll->shift.z = coll->old.z - zpos;            			// Leave Z as old value
				break;

			}
		}
		else
		{
			switch ( coll->quadrant )
			{
				case 0:
				case 2:
					coll->shift.x = coll->old.x - xpos;            			// Leave X as old value
					coll->shift.z = FindGridShift(zpos+zfront, zpos);		// Push Z
				break;

				case 1:
				case 3:
					coll->shift.x = FindGridShift(xpos+xfront, xpos);		// Push X
					coll->shift.z = coll->old.z - zpos;            			// Leave Z as old value
				break;

			}
		}

		coll->coll_type = COLL_FRONT;
		return;
	}

	if ( coll->front_ceiling >= coll->bad_ceiling )
	{
		coll->shift.x = coll->old.x - xpos;            	// Make X Y Z old positions..
		coll->shift.y = coll->old.y - ypos;
		coll->shift.z = coll->old.z - zpos;
		coll->coll_type = COLL_TOPFRONT;
		return;
	}

	/* Only do Left/Right collision types if no other?? */
	if ( coll->left_floor > coll->bad_pos ||
		 coll->left_floor < coll->bad_neg )
	{
		if (coll->left_type == SPLIT_TRI)
		{
			coll->shift.x = coll->old.x - xpos;            			// Leave X as old value
			coll->shift.z = coll->old.z - zpos;            			// Leave Z as old value
		}
		else
		{
			switch ( coll->quadrant )
			{
				case 0:
				case 2:
		 			coll->shift.x = FindGridShift( xpos+xleft,xpos+xfront );
				break;

				case 1:
				case 3:
		 			coll->shift.z = FindGridShift( zpos+zleft,zpos+zfront );
				break;
			}
		}
		coll->coll_type = COLL_LEFT;
		return;
	}
	if ( coll->right_floor > coll->bad_pos ||
		 coll->right_floor < coll->bad_neg )
	{
		if (coll->right_type == SPLIT_TRI)
		{
			coll->shift.x = coll->old.x - xpos;            			// Leave X as old value
			coll->shift.z = coll->old.z - zpos;            			// Leave Z as old value
		}
		else
		{
			switch ( coll->quadrant )
			{
				case 0:
				case 2:
					coll->shift.x = FindGridShift( xpos+xright,xpos+xfront );
				break;

				case 1:
				case 3:
					coll->shift.z = FindGridShift( zpos+zright,zpos+zfront );
				break;
			}
		}
		coll->coll_type = COLL_RIGHT;
		return;
	}
}



/******************************************************************************
 *			Find Shift in X from Source to Nearest Grid.Point towards Dest
 *****************************************************************************/
sint32	FindGridShift( sint32 src, sint32 dst )
{
	sint32	srcw,dstw;

	srcw = src>>WALL_SHIFT;                // Get Grid part we are in
	dstw = dst>>WALL_SHIFT;
	if ( srcw==dstw )                      // If in same Grid Square No Shifts
		return(0);

	src &= WALL_L-1;
	if (src == 0)
		return(0);

	if ( dstw>srcw)
		return( WALL_L - (src-1) );
	else
		return( -(src+1) );
}

/******************************************************************************
 *			Find Shift in X from Source to Nearest Grid.Point towards Dest added by Gibby for BIG_SLOPEs
 *****************************************************************************/
sint32	FindGridShift2( sint32 src, sint32 dst )
{
	sint32	srcw,dstw;

	srcw = src>>WALL_SHIFT;                // Get Grid part we are in
	dstw = dst>>WALL_SHIFT;

	if (srcw == dstw)                      // If in same Grid Square No Shifts
		return 0;

	src &= WALL_L-1;

	if (src == 0)
		return 0;

	if (dstw > srcw)
		return (WALL_L - src - 1);
	else
		return -src + 1;				// Gibby's shi(f)t fix..
}

/****************************************************************************************************************
 *			Given X,Y,Z,hite,room_number,radius of Object
 *          	find X and Z shifts due to Static Room Objects...
 *		----Relies on Coll->Quadrant and Coll->Room_Number being set----
 *				Returns TRUE if collided with Static Object
 **************************************************************************************************************/
int		CollideStaticObjects( COLL_INFO *coll, sint32 x, sint32 y, sint32 z, sint16 room_number, sint32 hite )
{
	int			i,j;
	ROOM_INFO	*r;
	MESH_INFO	*mesh;
	STATIC_INFO	*sinfo;
	sint32		xmin,xmax,ymin,ymax,zmin,zmax;
	sint32		inxmin,inxmax,inymin,inymax,inzmin,inzmax;
	PHD_VECTOR	shifter;
	sint32		shl,shr;


	coll->hit_static = 0;
	inxmin = x - coll->radius;
	inxmax = x + coll->radius;
	inymin = y - hite;
	inymax = y;
	inzmin = z - coll->radius;
	inzmax = z + coll->radius;
	shifter.x = shifter.y = shifter.z = 0;
	GetNearByRooms( x,y,z, coll->radius+50, hite+50, room_number );
	for ( i=0; i<number_draw_rooms; i++ )
	{
		r = &room[ draw_rooms[i] ];
		mesh = r->mesh;

		for ( j=r->num_meshes; j>0; j--, mesh++ )
		{
			sinfo = &static_objects[mesh->static_number];
			if ( (sinfo->flags&1) ) 					// If Bit 0 of flag set Skip collision
				continue;

			ymin = mesh->y + sinfo->y_minc;
			ymax = mesh->y + sinfo->y_maxc;
			switch ( mesh->y_rot )
			{
				default:								// Object Facing NORTH
					xmin = mesh->x + sinfo->x_minc;
					xmax = mesh->x + sinfo->x_maxc;
					zmin = mesh->z + sinfo->z_minc;
					zmax = mesh->z + sinfo->z_maxc;
				break;

				case 16384:								// Object Facing EAST
					xmin = mesh->x + sinfo->z_minc;
					xmax = mesh->x + sinfo->z_maxc;
					zmin = mesh->z - sinfo->x_maxc;
					zmax = mesh->z - sinfo->x_minc;
				break;

				case -32768:							// Object Facing SOUTH
					xmin = mesh->x - sinfo->x_maxc;
					xmax = mesh->x - sinfo->x_minc;
					zmin = mesh->z - sinfo->z_maxc;
					zmax = mesh->z - sinfo->z_minc;
				break;

				case -16384:							// Object Facing WEST
					xmin = mesh->x - sinfo->z_maxc;
					xmax = mesh->x - sinfo->z_minc;
					zmin = mesh->z + sinfo->x_minc;
					zmax = mesh->z + sinfo->x_maxc;
				break;
			}

			if ( inxmax<=xmin || inxmin>=xmax ||
			 	inymax<=ymin || inymin>=ymax ||
			 	inzmax<=zmin || inzmin>=zmax )		// If Dont Collide Exit Pronto....
				continue;

			shl = inxmax - xmin;
			shr = xmax - inxmin;
			if ( shl<shr )
				shifter.x = -shl;
			else
				shifter.x = shr;

//			shl = inymax - ymin;                    // warrrggghhh
//			shr = ymax - inymin;
//			if ( shl<shr )
//				shift->y = -shl;
//			else
//				shift->y = shr;

			shl = inzmax - zmin;
			shr = zmax - inzmin;
			if ( shl<shr )
				shifter.z = -shl;
			else
				shifter.z = shr;

			switch ( coll->quadrant )
			{
				case NORTH:
					if ( shifter.x > coll->radius || shifter.x < -coll->radius )
					{
						coll->shift.z = shifter.z;                      // Frontal Collision!!!
						coll->shift.x = coll->old.x - x;
						coll->coll_type = COLL_FRONT;
					}
					else if ( shifter.x>0 && shifter.x <= coll->radius )
					{
						coll->shift.x = shifter.x;                      // Side Collision
						coll->shift.z = 0;
						coll->coll_type = COLL_LEFT;
					}
					else if ( shifter.x<0 && shifter.x >= -coll->radius )
					{
						coll->shift.x = shifter.x;
						coll->shift.z = 0;
						coll->coll_type = COLL_RIGHT;
					}
				break;

				case EAST:
					if ( shifter.z > coll->radius || shifter.z < -coll->radius )
					{
						coll->shift.x = shifter.x;                      // Frontal Collision!!!
						coll->shift.z = coll->old.z - z;
						coll->coll_type = COLL_FRONT;
					}
					else if ( shifter.z>0 && shifter.z <= coll->radius )
					{
						coll->shift.z = shifter.z;                      // Side Collision
						coll->shift.x = 0;
						coll->coll_type = COLL_RIGHT;
					}
					else if ( shifter.z<0 && shifter.z >= -coll->radius )
					{
						coll->shift.z = shifter.z;
						coll->shift.x = 0;
						coll->coll_type = COLL_LEFT;
					}
				break;

				case SOUTH:
					if ( shifter.x > coll->radius || shifter.x < -coll->radius )
					{
						coll->shift.z = shifter.z;                      // Frontal Collision!!!
						coll->shift.x = coll->old.x - x;
						coll->coll_type = COLL_FRONT;
					}
					else if ( shifter.x>0 && shifter.x <= coll->radius )
					{
						coll->shift.x = shifter.x;                      // Side Collision
						coll->shift.z = 0;
						coll->coll_type = COLL_RIGHT;
					}
					else if ( shifter.x<0 && shifter.x >= -coll->radius )
					{
						coll->shift.x = shifter.x;
						coll->shift.z = 0;
						coll->coll_type = COLL_LEFT;
					}
				break;

				case WEST:
					if ( shifter.z > coll->radius || shifter.z < -coll->radius )
					{
						coll->shift.x = shifter.x;                      // Frontal Collision!!!
						coll->shift.z = coll->old.z - z;            	// Leave X as old value
						coll->coll_type = COLL_FRONT;
					}
					else if ( shifter.z>0 && shifter.z <= coll->radius )
					{
						coll->shift.z = shifter.z;                      // Side Collision
						coll->shift.x = 0;
						coll->coll_type = COLL_LEFT;
					}
					else if ( shifter.z<0 && shifter.z >= -coll->radius )
					{
						coll->shift.z = shifter.z;
						coll->shift.x = 0;
						coll->coll_type = COLL_RIGHT;
					}
				break;
			}
			coll->hit_static = 1;
			return(1);
		}
	}
	return(0);
}

/*******************************************************************************
 *				Get Nearby Rooms to current position. given height and radius
 *				Stashes them in draw_rooms[]
 *				sets number in number_draw_rooms
 *         only returns new rooms at corners and is limited by
 *         		constraints of GetFloor()
 ******************************************************************************/
void	GetNearByRooms( sint32 x, sint32 y, sint32 z, sint32 r, sint32 h, sint16 room_number )
{
	draw_rooms[0] = room_number;
	number_draw_rooms = 1;
	GetNewRoom( x+r, y, z+r, room_number );
	GetNewRoom( x-r, y, z+r, room_number );
	GetNewRoom( x+r, y, z-r, room_number );
	GetNewRoom( x-r, y, z-r, room_number );
	GetNewRoom( x+r, y-h, z+r, room_number );
	GetNewRoom( x-r, y-h, z+r, room_number );
	GetNewRoom( x+r, y-h, z-r, room_number );
	GetNewRoom( x-r, y-h, z-r, room_number );
}

void	GetNewRoom( sint32 x, sint32 y, sint32 z, sint16 room_number )
{
	int		i;

	GetFloor( x, y, z, &room_number );
	for ( i=0;i<number_draw_rooms;i++ )
	{
		if ( draw_rooms[i]==room_number )
			break;
	}
	if ( i==number_draw_rooms )
		draw_rooms[number_draw_rooms++] = room_number;
}

/********************************************************************
 *	Shift Item by amount recommended in Collision structure
 *******************************************************************/
void	ShiftItem( ITEM_INFO *item, COLL_INFO *coll )
{
	item->pos.x_pos += coll->shift.x;
	item->pos.y_pos += coll->shift.y;
	item->pos.z_pos += coll->shift.z;
	coll->shift.x = coll->shift.y = coll->shift.z = 0;		// reset shifts in case called again
}

/*******************************************************************
 *	Update Room That Lara is In  At Height from her Origin!!
 ******************************************************************/
void	UpdateLaraRoom( ITEM_INFO *item, int height )
{
	sint16		room_number;
	FLOOR_INFO 	*floor;
	sint32		x,y,z;

	x = item->pos.x_pos;
	y = item->pos.y_pos+height;
	z = item->pos.z_pos;
	room_number = item->room_number;
	floor = GetFloor( x, y, z, &room_number );
	item->floor = GetHeight( floor, x,y,z );
	if ( item->room_number != room_number)
		ItemNewRoom( lara.item_number, room_number );
}

/********************************************************************
 *		Get Tilt type of Floor Under Lara...
 *******************************************************************/
sint16	GetTiltType( FLOOR_INFO *floor, sint32 x, sint32 y, sint32 z)
{
	ROOM_INFO 	*r;
	sint16 		*data;
	uint16		index;
	sint16		type;
	sint16		tilts,dx,dz,retval,t0,t1,t2,t3,xoff,yoff;

	while (floor->pit_room != NO_ROOM)		// If over Pit goto Bottom....
	{
		if (CheckNoColFloorTriangle(floor, x, z) == 1)
			break;
		r = &room[floor->pit_room];
		floor = &r->floor[((z - r->z) >> WALL_SHIFT) + ((x - r->x) >> WALL_SHIFT) * r->x_size];
	}
	index = floor->index;

	// Gav: y coord must be on the floor for the slope to be effective (i.e. we're not on a bridge)
	if ( y+512 < ((sint32)floor->floor << 8))
		return (0);

	if ( floor->index )
	{
		data = &floor_data[floor->index];
		type = *(data) & DATA_TYPE;
		if ( type==TILT_TYPE )
			return( *(data+1) );

		if (type == SPLIT1 || type == SPLIT2 || type == NOCOLF1T || type == NOCOLF2T || type == NOCOLF1B || type == NOCOLF2B)
		{
			tilts = *(data+1);
			t0 = tilts&15;
			t1 = (tilts>>4)&15;
			t2 = (tilts>>8)&15;
			t3 = (tilts>>12)&15;

			dx = x&1023;
			dz = z&1023;

			if (	(type&DATA_TYPE) == SPLIT1 ||
			    	(type&DATA_TYPE) == NOCOLF1T ||
			    	(type&DATA_TYPE) == NOCOLF1B )
			{
				if (dx <= (1024-dz))	// In top triangle?
				{
					xoff = t2-t1;
					yoff = t0-t1;
				}
				else
				{
					xoff = t3-t0;
					yoff = t3-t2;
				}
			}
			else // SPLIT2
			{
				if (dx <= dz)	// In top triangle?
				{
					xoff = t2-t1;
					yoff = t3-t2;
				}
				else
				{
					xoff = t3-t0;
					yoff = t0-t1;
				}
			}
			return((xoff<<8)|(yoff&0xff));
		}
	}
	return(0);
}

/*************************************************************************************
 *			Handle Collision Of Lara with Nearby Items
 ************************************************************************************/
#define TARGET_DIST		(WALL_L*4)              // Up to this Distance more Complicated checks are made

void	LaraBaddieCollision( ITEM_INFO *laraitem, COLL_INFO *coll )
{
	int			i,x,y,z;
	ITEM_INFO	*item;
	OBJECT_INFO *object;
	sint16		roomies[20],numroom,*door;
	sint16		item_num,nex;

	laraitem->hit_status = 0;
	lara.hit_direction = -1;						// Reset Weve Been Hit Flag
	if ( laraitem->hit_points<=0 )					// If already Dead dont allow Baddie Collision
		return;
	numroom = 1;
	roomies[0] = laraitem->room_number;
	door = room[laraitem->room_number].door;		// Get rooms around laras position..
	if ( door )
	{
		for ( i=(int)*(door++); i>0; i-- )
		{
			roomies[numroom++] = *(door);
			door += 16;
		}
	}

	for ( i=0; i<numroom; i++ )
	{
		item_num = room[ roomies[i] ].item_number;						// Only do collision with Items on Draw list
		while ( item_num!=NO_ITEM )
		{
			item = &items[ item_num ];
			nex = item->next_item;                              		// Store next Item in list as Current may be deleted!!!
			if ( item->collidable && item->status!=INVISIBLE )			// Object Isnt Collidable so Exit now
			{
				object = &objects[item->object_number];
				if ( object->collision )   								// If collision priority Zero exit now
				{
					x = laraitem->pos.x_pos - item->pos.x_pos;			// Get Vector from Baddie to Lara
					y = laraitem->pos.y_pos - item->pos.y_pos;
					z = laraitem->pos.z_pos - item->pos.z_pos;
					if ( x > -TARGET_DIST && x < TARGET_DIST &&         // if fairly near to player
			 	 		 z > -TARGET_DIST && z < TARGET_DIST &&         // then Involve more Rigorous Collision
				 		 y > -TARGET_DIST && y < TARGET_DIST )          // detection routine
							(*object->collision)( item_num,laraitem,coll );
				}
			}
			item_num = nex;
		}
	}
//	if ( coll->enable_spaz && lara.spaz_effect_count )
	if ( lara.spaz_effect_count )
		EffectSpaz( laraitem,coll );
	if ( lara.hit_direction==-1 )					// If we havent been hit
		lara.hit_frame = 0;                         // reset hit frame to zero
	Inventory_Chosen = -1;
#undef TARGET_DIST
}

/******************************************************************************
 *		Kick Off  Lara spazzing due to Effect colliding with her
 *****************************************************************************/
void	EffectSpaz( ITEM_INFO *laraitem, COLL_INFO *coll )
{
	FX_INFO		*fx;
	sint32		x,z;
	PHD_ANGLE	hitang;

	fx = lara.spaz_effect;
	x = lara.spaz_effect->pos.x_pos - laraitem->pos.x_pos;
	z = lara.spaz_effect->pos.z_pos - laraitem->pos.z_pos;
	hitang = laraitem->pos.y_rot - (32768 + phd_atan(z,x));
	lara.hit_direction = (uint16)(hitang+0x2000)/0x4000;    // Get direction weve hit baddie
	if ( !lara.hit_frame )
		SoundEffect( 31, &laraitem->pos, 0 );          	// Play Injury sample if at start of Spaz
	lara.hit_frame++;                                       // Increase Spaz out frame number..
	if ( lara.hit_frame>34 )
		lara.hit_frame=34;
	lara.spaz_effect_count--;
}


/******************************************************************************
 *			Generic Baddie Collision Routine...
 *****************************************************************************/
void	CreatureCollision( sint16 item_num, ITEM_INFO *laraitem, COLL_INFO *coll )
{
	/* CreatureCollision: cause Lara to 'spaz', and don't push her away as far as objects */
	ITEM_INFO	*item;

	item = &items[ item_num ];
	if ( !TestBoundsCollide( item,laraitem,coll->radius ) )
		return;
	if ( !TestCollision( item, laraitem ) )
		return;
	if ( coll->enable_baddie_push && lara.water_status != LARA_UNDERWATER && !lara.water_status != LARA_SURFACE )
		ItemPushLara( item, laraitem, coll, coll->enable_spaz,0 );
}

/******************************************************************************
 *					Non Movable Object Collision
 *     	eg Dead Baddies,Inactive Things Note these Still push Lara out of Way
 *****************************************************************************/
void	ObjectCollision( sint16 item_num, ITEM_INFO *laraitem, COLL_INFO *coll )
{
	/* ObjectCollision: don't spaz Lara, but push her away to the edge of their bounds */
	ITEM_INFO	*item;

	item = &items[ item_num ];
	if ( !TestBoundsCollide( item,laraitem,coll->radius ) )
		return;
	if ( !TestCollision( item, laraitem ) )
		return;
	if ( coll->enable_baddie_push )
		ItemPushLara( item, laraitem, coll, 0,1 );
}


/****************************************************************************
 *				Do Lara-Door Collision
 *  	If the door is Moving do Creature style collision
 *			else treat it as a normal object.
 ***************************************************************************/
void	DoorCollision( sint16 item_num, ITEM_INFO *laraitem, COLL_INFO *coll )
{
	ITEM_INFO	*item;

	item = &items[item_num];
	if ( !TestBoundsCollide( item,laraitem,coll->radius ) )
		return;
	if ( !TestCollision( item, laraitem ) )
		return;

	if ( coll->enable_baddie_push )
	{
		if ( item->current_anim_state!=item->goal_anim_state )				// Door is moving so
			ItemPushLara( item, laraitem, coll, coll->enable_spaz,1 );      // Make Lara spaz
		else                                                                // shift her without spaz
			ItemPushLara( item, laraitem, coll, 0,1 );
	}
}

/****************************************************************************
 *		Do Lara-Door Collision
 ***************************************************************************/
void	TrapCollision( sint16 item_num, ITEM_INFO *laraitem, COLL_INFO *coll )
{
	ITEM_INFO	*item;

	item = &items[item_num];

	if ( item->status==ACTIVE )
	{
		if ( !TestBoundsCollide( item,laraitem,coll->radius ) )
			return;
		TestCollision( item, laraitem );     // call this so Traps touch_bits are updated
	}
	else if ( item->status!=INVISIBLE )
		ObjectCollision( item_num,laraitem,coll );
}

/******************************************************************************
 *					Push Lara by baddie
 *****************************************************************************/
void	ItemPushLara( ITEM_INFO *item, ITEM_INFO *laraitem, COLL_INFO *coll, int spazon, int bigpush )
{
	int			x,z,rx,rz,c,s;
	int			minx,maxx,minz,maxz;
	int			l,r,t,b,ax,az;
	OBJECT_INFO *object;
	PHD_ANGLE	hitang;
	sint16 		*bounds;


 	x = laraitem->pos.x_pos - item->pos.x_pos;		// Get Vector from Baddie to Lara
 	z = laraitem->pos.z_pos - item->pos.z_pos;
	c = phd_cos( item->pos.y_rot );					// Get Baddies Rotation
	s = phd_sin( item->pos.y_rot );
	rx = ( c*x - s*z ) >> W2V_SHIFT;            	// Rotate Lara Vector into Baddie Frame
	rz = ( c*z + s*x ) >> W2V_SHIFT;

 	object = &objects[item->object_number];
 	bounds = GetBestFrame( item );
	minx = *(bounds+0);          					// push lara midpoint to edge of baddie
	maxx = *(bounds+1);
	minz = *(bounds+4);
	maxz = *(bounds+5);
	if ( bigpush )
	{
		minx -= coll->radius;
		maxx += coll->radius;
		minz -= coll->radius;
		maxz += coll->radius;
	}
	if ( rx>=minx && rx<=maxx && rz>=minz && rz<=maxz )
	{
		l = rx - minx;
		r = maxx - rx;
		t = maxz - rz;
		b = rz - minz;

		if ( l<=r && l<=t && l<=b )   			// Find Nearest Edge of Collide Box
			rx -= l;                            // and push Lara Vector to this Edge
		else if ( r<=l && r<=t && r<=b )
			rx += r;
		else if ( t<=l && t<=r && t<=b )
			rz += t;
		else
			rz -= b;
		ax = (c*rx + s*rz)>>W2V_SHIFT;
		az = (c*rz - s*rx)>>W2V_SHIFT;

		laraitem->pos.x_pos = item->pos.x_pos + ax;
		laraitem->pos.z_pos = item->pos.z_pos + az;

		rx = (*(bounds+0) + *(bounds+1))/2;			  // Get Hit Angle..
		rz = (*(bounds+4) + *(bounds+5))/2;
		x -= ( c*rx + s*rz ) >> W2V_SHIFT;            // Get Vector from Lara to
		z -= ( c*rz - s*rx ) >> W2V_SHIFT;            // Midpoint of Baddie..
		if ( spazon && bounds[3]-bounds[2] > STEP_L )
		{
			hitang = laraitem->pos.y_rot - (32768 + phd_atan(z,x));
			lara.hit_direction = (uint16)(hitang+0x2000)/0x4000;    // Get direction weve hit baddie
			if ( !lara.hit_frame )
				SoundEffect( 31, &laraitem->pos, 0 );          	// Play Injury sample if at start of Spaz
			lara.hit_frame++;                                       // Increase Spaz out frame number..
			if ( lara.hit_frame>34 )
				lara.hit_frame=34;
		}
		coll->bad_pos = NO_BAD_POS;
		coll->bad_neg = -STEPUP_HEIGHT;
		coll->bad_ceiling = 0;
		hitang = coll->facing;
		coll->facing = phd_atan( (laraitem->pos.z_pos-coll->old.z),(laraitem->pos.x_pos-coll->old.x) );
		GetCollisionInfo( coll, laraitem->pos.x_pos,laraitem->pos.y_pos,laraitem->pos.z_pos,
						laraitem->room_number,LARA_HITE );
		coll->facing = hitang;
		if ( coll->coll_type!=COLL_NONE )                  	  // Move Lara cos of Baddie
		{
			laraitem->pos.x_pos = coll->old.x;                // If collided with Wall
//			laraitem->pos.y_pos = coll->old.y;                // use old position else
			laraitem->pos.z_pos = coll->old.z;                // baddies push lara out of Walls
		}
		else
		{
			coll->old.x = laraitem->pos.x_pos;			// Update Position cos no Bad Collisions...
			coll->old.y = laraitem->pos.y_pos;
			coll->old.z = laraitem->pos.z_pos;
			UpdateLaraRoom( laraitem,-10 );
		}
	}
}


/*****************************************************************************
 *			Do General Check to see if LaraRadius  intersect items Bounds
 ****************************************************************************/
int		TestBoundsCollide( ITEM_INFO *item, ITEM_INFO *laraitem, sint32 radius )
{
	sint16 		*larabounds,*bounds;
	OBJECT_INFO *object;
	sint32		c,s,rx,rz,x,z;
	sint32		minx,maxx,minz,maxz;


	object = &objects[item->object_number];
	bounds = GetBestFrame( item );
	object = &objects[laraitem->object_number];
	larabounds = GetBestFrame( laraitem );
	if ( item->pos.y_pos + *(bounds+3) <= laraitem->pos.y_pos + *(larabounds+2) ||
		item->pos.y_pos + *(bounds+2) >= laraitem->pos.y_pos + *(larabounds+3) )
			return(0);

	c = phd_cos( item->pos.y_rot );						// Get Baddies Rotation
	s = phd_sin( item->pos.y_rot );
	x = laraitem->pos.x_pos - item->pos.x_pos;			// Get Vector from Baddie to Lara
	z = laraitem->pos.z_pos - item->pos.z_pos;
	rx = ( c*x - s*z ) >> W2V_SHIFT;            		// Rotate Lara Vector into Baddie Frame
	rz = ( c*z + s*x ) >> W2V_SHIFT;
	minx = *(bounds+0) - radius;           				// Now if Bounds Overlap then call
	maxx = *(bounds+1) + radius;							// appropriate collision routine
	minz = *(bounds+4) - radius;
	maxz = *(bounds+5) + radius;
	if ( rx>=minx && rx<=maxx && rz>=minz && rz<=maxz )
		return(1);
	return(0);
}


/****************************************************************************
 *			Test Laras MidPoint Positioning
 *				Bounds = MinX,MaxX  relative to Object Origin...
 *						 MinY,MaxY
 *						 MinZ,MaxZ
 *						 MinXrot,MaxXrot
 *						 MinYrot,MaxYrot
 *						 MinZrot,MaxZrot
 ***************************************************************************/
int		TestLaraPosition( sint16 *bounds, ITEM_INFO *item, ITEM_INFO *laraitem )
{
	sint32		x,y,z;
	sint32		rx,ry,rz;
	sint32		*mptr;
	PHD_ANGLE	xrotrel,yrotrel,zrotrel;


	xrotrel = laraitem->pos.x_rot - item->pos.x_rot;
	yrotrel = laraitem->pos.y_rot - item->pos.y_rot;
	zrotrel = laraitem->pos.z_rot - item->pos.z_rot;
//Log("ROTS: %d %d %d", xrotrel, yrotrel, zrotrel);

	if ( xrotrel < *(bounds+6) || xrotrel > *(bounds+7) ||				// If Rotations out of
		 yrotrel < *(bounds+8) || yrotrel > *(bounds+9) || 				// range return 0
		 zrotrel < *(bounds+10) || zrotrel > *(bounds+11) )
			return(0);
	x = laraitem->pos.x_pos - item->pos.x_pos;							// Get Vector from Baddie to Lara
	y = laraitem->pos.y_pos - item->pos.y_pos;
	z = laraitem->pos.z_pos - item->pos.z_pos;
//Log("XYZ: %d %d %d", x, y, z);
	phd_PushUnitMatrix();
	phd_RotYXZ( item->pos.y_rot, item->pos.x_rot, item->pos.z_rot );
	mptr = phd_mxptr;
	rx = ( *(mptr+M00)*x + *(mptr+M10)*y + *(mptr+M20)*z )>>W2V_SHIFT;  // Transposed Rotation!!
	ry = ( *(mptr+M01)*x + *(mptr+M11)*y + *(mptr+M21)*z )>>W2V_SHIFT;
	rz = ( *(mptr+M02)*x + *(mptr+M12)*y + *(mptr+M22)*z )>>W2V_SHIFT;
//Log("R: %d %d %d", rx, ry, rz);
	phd_PopMatrix();
	if ( rx < (sint32)*(bounds+0) || rx > (sint32)*(bounds+1) ||        // Reject if Not within Bounds
		 ry < (sint32)*(bounds+2) || ry > (sint32)*(bounds+3) ||
		 rz < (sint32)*(bounds+4) || rz > (sint32)*(bounds+5) )
			return(0);

	return(1);
}

/****************************************************************************
 *			Move Laras Positioning Relative to an Object
 *				Bounds = MinX,MaxX  relative to Object Origin...
 *						 MinY,MaxY
 *						 MinZ,MaxZ
 *						 MinYrot,MaxYrot
 *						 MinXrot,MaxXrot
 ***************************************************************************/
void	AlignLaraPosition( PHD_VECTOR *vec, ITEM_INFO *item, ITEM_INFO *laraitem )
{
	sint16 room_number;
	FLOOR_INFO *floor;
	sint32 *mptr, x, y, z, height, ceiling;

	laraitem->pos.x_rot = item->pos.x_rot;
	laraitem->pos.y_rot = item->pos.y_rot;
	laraitem->pos.z_rot = item->pos.z_rot;
	phd_PushUnitMatrix();
	phd_RotYXZ( item->pos.y_rot, item->pos.x_rot, item->pos.z_rot );
	mptr = phd_mxptr;
	//laraitem->pos.x_pos = item->pos.x_pos + (( *(mptr+M00)*vec->x + *(mptr+M01)*vec->y + *(mptr+M02)*vec->z )>>W2V_SHIFT);
	//laraitem->pos.y_pos = item->pos.y_pos + (( *(mptr+M10)*vec->x + *(mptr+M11)*vec->y + *(mptr+M12)*vec->z )>>W2V_SHIFT);
	//laraitem->pos.z_pos = item->pos.z_pos + (( *(mptr+M20)*vec->x + *(mptr+M21)*vec->y + *(mptr+M22)*vec->z )>>W2V_SHIFT);
	x = item->pos.x_pos + (( *(mptr+M00)*vec->x + *(mptr+M01)*vec->y + *(mptr+M02)*vec->z )>>W2V_SHIFT);
	y = item->pos.y_pos + (( *(mptr+M10)*vec->x + *(mptr+M11)*vec->y + *(mptr+M12)*vec->z )>>W2V_SHIFT);
	z = item->pos.z_pos + (( *(mptr+M20)*vec->x + *(mptr+M21)*vec->y + *(mptr+M22)*vec->z )>>W2V_SHIFT);
	phd_PopMatrix();

	/* SONY BUG FIX A16: Check new position isn't bad (if it is, then don't move Lara at all) */
	room_number = laraitem->room_number;
	floor = GetFloor(x, y, z, &room_number);
	height = GetHeight(floor, x, y, z);
	ceiling = GetCeiling(floor,x,y,z);
	if (ABS(height - laraitem->pos.y_pos) > STEP_L ||
		ABS(ceiling - laraitem->pos.y_pos) < LARA_HITE) // SONY BUG FIX A16 again - Stop Lara snapping into sloped ceiling/floor recesses
		return;

	laraitem->pos.x_pos = x;
	laraitem->pos.y_pos = y;
	laraitem->pos.z_pos = z;
}

/******************************************************************************
 *				Move Laras Position UnderWater from
 *
 *****************************************************************************/
#define MOVE_SPEED	16
#define MOVE_ANG	2*ONE_DEGREE

int		MoveLaraPosition( PHD_VECTOR *vec, ITEM_INFO *item, ITEM_INFO *laraitem )
{
	sint16 room_number;
	FLOOR_INFO *floor;
	PHD_3DPOS	dest;
	sint32		*mptr, x,y,z,height,dist;

	dest.x_rot = item->pos.x_rot;
	dest.y_rot = item->pos.y_rot;
	dest.z_rot = item->pos.z_rot;
	phd_PushUnitMatrix();
	phd_RotYXZ( item->pos.y_rot, item->pos.x_rot, item->pos.z_rot );
	mptr = phd_mxptr;
	dest.x_pos = item->pos.x_pos + (( *(mptr+M00)*vec->x + *(mptr+M01)*vec->y + *(mptr+M02)*vec->z )>>W2V_SHIFT);
	dest.y_pos = item->pos.y_pos + (( *(mptr+M10)*vec->x + *(mptr+M11)*vec->y + *(mptr+M12)*vec->z )>>W2V_SHIFT);
	dest.z_pos = item->pos.z_pos + (( *(mptr+M20)*vec->x + *(mptr+M21)*vec->y + *(mptr+M22)*vec->z )>>W2V_SHIFT);
	phd_PopMatrix();

	if (item->object_number==FLARE_ITEM)
	{
		// SONY BUG FIX A16 - Stop Lara teleporting when picking up flare back against a wall
		room_number = laraitem->room_number;
		floor = GetFloor(dest.x_pos, dest.y_pos, dest.z_pos, &room_number);
		height = GetHeight(floor, dest.x_pos, dest.y_pos, dest.z_pos);
		if (ABS(height - laraitem->pos.y_pos) > STEP_L*2 )
			return 0;
		else
		{
		 	x = dest.x_pos - laraitem->pos.x_pos;
			y = dest.y_pos - laraitem->pos.y_pos;
			z = dest.z_pos - laraitem->pos.z_pos;
			dist = phd_sqrt( x*x + y*y + z*z );
			if (dist<128)
				return 1;	// If Lara is close enough then get it anyway
		}
		//
	}

	return( Move3DPosTo3DPos( &laraitem->pos, &dest, MOVE_SPEED, MOVE_ANG ) );
}

/*******************************************************************************
 *				Move Source Position (Vector & Angles) towards
 *                   Target position..
 *		Returns TRUE if source has reached position
 ******************************************************************************/
int		Move3DPosTo3DPos( PHD_3DPOS *srcpos, PHD_3DPOS *destpos, int velocity, PHD_ANGLE angadd )
{
	int			dist,x,y,z;
	PHD_ANGLE	angdif;


	x = destpos->x_pos - srcpos->x_pos;
	y = destpos->y_pos - srcpos->y_pos;
	z = destpos->z_pos - srcpos->z_pos;
	dist = phd_sqrt( x*x + y*y + z*z );
	if ( velocity>=dist )
	{
		srcpos->x_pos = destpos->x_pos;       	// If already close to Dest
		srcpos->y_pos = destpos->y_pos;        	// then make source=dest
		srcpos->z_pos = destpos->z_pos;
	}
	else
	{
		srcpos->x_pos += (x*velocity)/dist;		// else make nearer dest
		srcpos->y_pos += (y*velocity)/dist;     // by velocity amount
		srcpos->z_pos += (z*velocity)/dist;
	}

	angdif = destpos->x_rot - srcpos->x_rot;
	if ( angdif>angadd )
		srcpos->x_rot += angadd;
	else if ( angdif<-angadd )
		srcpos->x_rot -= angadd;
	else
		srcpos->x_rot = destpos->x_rot;

	angdif = destpos->y_rot - srcpos->y_rot;
	if ( angdif>angadd )
		srcpos->y_rot += angadd;
	else if ( angdif<-angadd )
		srcpos->y_rot -= angadd;
	else
		srcpos->y_rot = destpos->y_rot;

	angdif = destpos->z_rot - srcpos->z_rot;
	if ( angdif>angadd )
		srcpos->z_rot += angadd;
	else if ( angdif<-angadd )
		srcpos->z_rot -= angadd;
	else
		srcpos->z_rot = destpos->z_rot;


	if ( srcpos->x_pos==destpos->x_pos &&		// Have We reached Destination
		 srcpos->y_pos==destpos->y_pos &&       // Position yet???
		 srcpos->z_pos==destpos->z_pos &&
		 srcpos->x_rot==destpos->x_rot &&
		 srcpos->y_rot==destpos->y_rot &&
		 srcpos->z_rot==destpos->z_rot )
			return(1);
	else
		return(0);
}


