/*********************************************************************************************/
/*                                                                                           */
/* Camera Control Code                                                                       */
/*                                                                                           */
/*********************************************************************************************/

/*---------------------------------------------------------------------------
 *	Import Headers
\*--------------------------------------------------------------------------*/

#ifdef PSX_VERSION
#include "..\spec_psx\typedefs.h"
#include "..\spec_psx\specific.h"
#include "..\spec_psx\maths.h"
#else
#include "..\specific\standard.h"
#include "..\specific\global.h"
#endif

#include "lara.h"
#include "laraanim.h"
#include "camera.h"
#include "control.h"
#include "sound.h"
#include "savegame.h"
#include "cinema.h"
#include "effect2.h"

#include "..\spec_psx\psxinput.h"
#include "..\game\input.h"

/*---------------------------------------------------------------------------
 *	Constants
\*--------------------------------------------------------------------------*/

long	CameraCollisionBounds(GAME_VECTOR *ideal, long push, long yfirst);
long mgLOS(GAME_VECTOR *start, GAME_VECTOR *target, long push);

#define COM_SQUARE SQUARE(WALL_L/2)
#define MIN_SQUARE SQUARE(WALL_L/3)
#define MIN_CAM_ANGLE (ONE_DEGREE/2)

#define GROUND_SHIFT (STEP_L)

#define MAX_ELEVATION (ONE_DEGREE*85)

#define NORMAL_ELEVATION 	-(ONE_DEGREE*10)
#define COMBAT_ELEVATION 	-(ONE_DEGREE*15)
#define MAX_CAMERA_PUSH		200
#define MIN_CAMERA_SNAP		768
#define MAX_HEIGHT_CHANGE	1536

//#define CHECK_ILLEGAL_CAMPOS

/* Look around limits for automatic looking at targets */
#define MAX_LOOKX_ROTATION	-(75*ONE_DEGREE)
#define MIN_LOOKX_ROTATION	(55*ONE_DEGREE)
#define MAX_LOOKY_ROTATION	(80*ONE_DEGREE)
#define MAX_HEAD_ROTATION 	(50*ONE_DEGREE)
//#define MAX_HEAD_TILT	  	(25*ONE_DEGREE)
//#define MIN_HEAD_TILT 	  	(-45*ONE_DEGREE)
#define MAX_HEAD_TILT	  	(85*ONE_DEGREE)
#define MIN_HEAD_TILT 	  	(-85*ONE_DEGREE)
#define HEAD_TURN 		  	(4*ONE_DEGREE) //double what's needed because lara.c will reset it 2 degrees

/* Divider speeds for cameras to move to target i.e. pos += distance / speed */
#define CHASE_SPEED 10
#define COMBAT_SPEED 8
#define COMBAT_DISTANCE (WALL_L*3/2)
#define LOOK_SPEED   4

#define	MAX_CAMERA_MOVE	384
#define	MAX_LOOKCAMERA_MOVE	224

typedef struct
{
	sint16 current_anim_state;
	sint16 goal_anim_state;
	PHD_3DPOS pos;
	PHD_3DPOS pos2;
} OLD_CAMERA;

/*---------------------------------------------------------------------------
 *	Globals
\*--------------------------------------------------------------------------*/

sint32	oldtx,oldty,oldtz;
sint32	TooFarCount = 0;
sint32	CombatShift = 0;

uchar camerasnaps = 0;

CAMERA_INFO camera;

GAME_VECTOR	static_lookcamp;	// For look camera.
GAME_VECTOR	static_lookcamt;	// For look camera.
OLD_CAMERA	old_cam;			// For normal camera to stop juddering.
GAME_VECTOR	last_ideal;
GAME_VECTOR	last_target;

char UseForcedFixedCamera;
GAME_VECTOR ForcedFixedCamera;

long NumFixedCameras;

/*********************************** FUNCTION CODE *******************************************/

void InitialiseCamera(void)
{
	/* Initialise position of camera */
	camera.target.x = last_target.x = lara_item->pos.x_pos;
	camera.target.y = last_target.y = camera.shift = lara_item->pos.y_pos-WALL_L;
	camera.target.z = last_target.z = lara_item->pos.z_pos;
	camera.target.room_number = last_target.room_number = lara_item->room_number;

	camera.pos.x = camera.target.x;
	camera.pos.y = camera.target.y;
	camera.pos.z = camera.target.z-100;
	camera.pos.room_number = camera.target.room_number;

	camera.target_distance = WALL_L*3/2;
	camera.item = NULL;

	camera.number_frames = 1;
	if (!lara.extra_anim)
		camera.type = CHASE_CAMERA;
	camera.speed = 1;
	camera.flags = 0;

	camera.bounce = 0;

	/* No fixed camera */
	camera.number = NO_CAMERA;
	camera.fixed_camera = 0;
	AlterFOV(GAME_FOV*ONE_DEGREE);

	UseForcedFixedCamera = 0;

	CalculateCamera();
}


void MoveCamera(GAME_VECTOR *ideal, int speed)
{
	FLOOR_INFO *floor;
	sint32 height, ceiling, shake;

#define STRAIGHT_MOVE
#ifdef STRAIGHT_MOVE

	/* Move along straight line to ideal position */

// Outrageous IF statement to stop camera wobble.

	if (	old_cam.pos.x_rot != lara_item->pos.x_rot ||
		old_cam.pos.y_rot != lara_item->pos.y_rot ||
		old_cam.pos.z_rot != lara_item->pos.z_rot ||
		old_cam.pos2.x_rot != lara.head_x_rot ||
		old_cam.pos2.y_rot != lara.head_y_rot ||
		old_cam.pos2.x_pos != lara.torso_x_rot ||
		old_cam.pos2.y_pos != lara.torso_y_rot ||
		old_cam.pos.x_pos != lara_item->pos.x_pos ||
		old_cam.pos.y_pos != lara_item->pos.y_pos ||
		old_cam.pos.z_pos != lara_item->pos.z_pos ||
		old_cam.current_anim_state != lara_item->current_anim_state ||
		old_cam.goal_anim_state != lara_item->goal_anim_state ||
		camera.old_type != camera.type)
	{
		old_cam.pos.x_rot = lara_item->pos.x_rot;
		old_cam.pos.y_rot = lara_item->pos.y_rot;
		old_cam.pos.z_rot = lara_item->pos.z_rot;
		old_cam.pos2.x_rot = lara.head_x_rot;	//
		old_cam.pos2.y_rot = lara.head_y_rot;	// For combat camera.
		old_cam.pos2.x_pos = lara.torso_x_rot;  //
		old_cam.pos2.y_pos = lara.torso_y_rot;  //
		old_cam.pos.x_pos = lara_item->pos.x_pos;
		old_cam.pos.y_pos = lara_item->pos.y_pos;
		old_cam.pos.z_pos = lara_item->pos.z_pos;
		old_cam.current_anim_state = lara_item->current_anim_state;
		old_cam.goal_anim_state = lara_item->goal_anim_state;

		last_ideal.x = ideal->x;
		last_ideal.y = ideal->y;
		last_ideal.z = ideal->z;
		last_ideal.room_number = ideal->room_number;
	}
	else
	{
		ideal->x = last_ideal.x;
		ideal->y = last_ideal.y;
		ideal->z = last_ideal.z;
		ideal->room_number = last_ideal.room_number;
	}

	camera.pos.x += (ideal->x - camera.pos.x) / speed;
	camera.pos.y += (ideal->y - camera.pos.y) / speed;
	camera.pos.z += (ideal->z - camera.pos.z) / speed;
	camera.pos.room_number = ideal->room_number;

#else
	/* Move along radius to ideal position */
	sint32 x, z, distance, c_distance;
	sint16 angle, c_angle;

	/* Move along straight line to ideal position */

	z = ideal->z - camera.target.z;
	x = ideal->x - camera.target.x;
	angle = phd_atan(z, x);
	distance = mSqrt(z*z + x*x);

	// TODO: + need current angle and rotation, so worth adding to camera structure
	z = camera.pos.z - camera.target.z;
	x = camera.pos.x - camera.target.x;
	c_angle = phd_atan(z, x);
	c_distance = mSqrt(z*z + x*x);

	/* Linearly change both angle and distance to ideal values */
	angle -= c_angle;
	c_angle += angle / speed;
	c_distance += (distance - c_distance) / speed;
	camera.pos.z = camera.target.z + (c_distance * phd_cos(c_angle) >> W2V_SHIFT);
	camera.pos.x = camera.target.x + (c_distance * phd_sin(c_angle) >> W2V_SHIFT);
	camera.pos.y += (ideal->y - camera.pos.y) / speed;
	camera.pos.room_number = ideal->room_number;
#endif

	/* Add bounce effect */
	if (camera.bounce)
	{
		if (camera.bounce > 0)
		{
			camera.pos.y += camera.bounce;
			camera.target.y += camera.bounce;
			camera.bounce = 0;
		}
		else
		{
			/* Shake camera randomly */
			shake = (GetRandomControl()-0x4000) * camera.bounce / 0x7fff;
			camera.pos.x += shake;
			camera.target.y += shake;
			shake = (GetRandomControl()-0x4000) * camera.bounce / 0x7fff;
			camera.pos.y += shake;
			camera.target.y += shake;
			shake = (GetRandomControl()-0x4000) * camera.bounce / 0x7fff;
			camera.pos.z += shake;
			camera.target.z += shake;
			//shake = (abs(shake)<<8)&0xff00;
			//RUMBLE(shake>>4, shake, 20, 0x100, 40);
			//VIBRATE(0x1f00>>4, 0x1f00, 20, 0x100, 40);
			camera.bounce += 5;
		}
	}

	{
		sint16	room_number;
		long		wx,wy,wz;

		wx = camera.pos.x;
		wy = camera.pos.y;
		wz = camera.pos.z;

		room_number = camera.pos.room_number;
		floor = GetFloor(wx, wy+256, wz, &room_number);
		if (room[room_number].flags & SWAMP)                   //
		{											// Do swamp room check.
			wy = room[room_number].y-256;                     //
			floor = GetFloor(wx, wy, wz, &camera.pos.room_number);
		}

		room_number = camera.pos.room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		height=GetHeight(floor,wx,wy,wz);
		ceiling=GetCeiling(floor,wx,wy,wz);
		if (wy < ceiling || wy > height)
		{
			long		dx,dy,dz;
			GAME_VECTOR	temp1,temp2;

			//LOS(&camera.target,&camera.pos);
			mgLOS(&camera.target,&camera.pos,0);

			dx = abs(camera.pos.x - ideal->x);
			dy = abs(camera.pos.y - ideal->y);
			dz = abs(camera.pos.z - ideal->z);

			if (dx < MIN_CAMERA_SNAP && dy < MIN_CAMERA_SNAP && dz < MIN_CAMERA_SNAP)
			{
				temp1.x = camera.pos.x;
				temp1.y = camera.pos.y;
				temp1.z = camera.pos.z;
				temp1.room_number = camera.pos.room_number;
				temp2.x = ideal->x;
				temp2.y = ideal->y;
				temp2.z = ideal->z;
				temp2.room_number = ideal->room_number;
				if (!(mgLOS(&temp2,&temp1,0)))
				//if (!(LOS(&temp2,&temp1)))
				{
					camerasnaps++;
					if (camerasnaps >= 8)
					{
						camera.pos.x = ideal->x;
						camera.pos.y = ideal->y;
						camera.pos.z = ideal->z;
						camera.pos.room_number = ideal->room_number;
						camerasnaps=0;
					}
				}
			}
		}
	}

// Mega super check to see if camera is still in an illegal position,
// if it is then make it the ideal position which, in theory, should never be incorrect.

	{
		sint16	room_number;
		long		wx,wy,wz;

		wx = camera.pos.x;
		wy = camera.pos.y;
		wz = camera.pos.z;
		room_number = camera.pos.room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		height=GetHeight(floor,wx,wy,wz);
		ceiling=GetCeiling(floor,wx,wy,wz);

		if (wy-255 < ceiling && wy+255 > height && ceiling<height && ceiling != NO_HEIGHT && height != NO_HEIGHT)
			camera.pos.y = (height+ceiling) >> 1;
		else if (wy+255 > height && ceiling<height && ceiling != NO_HEIGHT && height != NO_HEIGHT)
			camera.pos.y = height-255;
		else if (wy-255 < ceiling && ceiling<height && ceiling != NO_HEIGHT && height != NO_HEIGHT)
			camera.pos.y = ceiling+255;
		else if (ceiling >= height || height == NO_HEIGHT || ceiling == NO_HEIGHT)
		{
			camera.pos.x = ideal->x;
			camera.pos.y = ideal->y;
			camera.pos.z = ideal->z;
			camera.pos.room_number = ideal->room_number;
		}
	}

#ifdef CHECK_ILLEGAL_CAMPOS
	{
		sint16	room_number;
		long		wx,wy,wz;

		wx = camera.pos.x;
		wy = camera.pos.y;
		wz = camera.pos.z;
		room_number = camera.pos.room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		height=GetHeight(floor,wx,wy,wz);
		ceiling=GetCeiling(floor,wx,wy,wz);
		if (wy < ceiling || wy > height || ceiling >= height || height == NO_HEIGHT || ceiling == NO_HEIGHT)
			printf("Camera fucked! Room: %d X:%d Y:%d Z:%d\n",room_number,wx,wy,wz);
	}
#endif

  	GetFloor(camera.pos.x, camera.pos.y, camera.pos.z, &camera.pos.room_number);
	phd_LookAt(camera.pos.x, camera.pos.y, camera.pos.z, camera.target.x, camera.target.y, camera.target.z, 0);

	/* For stereo sound effects need to know camera facing */
#ifdef PC_VERSION
	if (camera.mike_at_lara)
	{
		camera.actual_angle=lara_item->pos.y_rot+lara.head_y_rot+lara.torso_y_rot;
		camera.mike_pos.x=lara_item->pos.x_pos;
		camera.mike_pos.y=lara_item->pos.y_pos;
		camera.mike_pos.z=lara_item->pos.z_pos;
	}
	else
#endif
	{
		camera.actual_angle = phd_atan(camera.target.z - camera.pos.z, camera.target.x - camera.pos.x);
		camera.mike_pos.x=camera.pos.x+((phd_persp*phd_sin(camera.actual_angle))>>W2V_SHIFT);
		camera.mike_pos.z=camera.pos.z+((phd_persp*phd_cos(camera.actual_angle))>>W2V_SHIFT);
		camera.mike_pos.y=camera.pos.y;
	}

	camera.old_type = camera.type;
}


void ChaseCamera(ITEM_INFO *item)
{
	/* Standard follow Lara camera */
	FLOOR_INFO	*floor;
	GAME_VECTOR 	ideal;
	GAME_VECTOR	ideals[5];
	GAME_VECTOR	temp[2];
	sint32 		distance,lp,dx,dz,farthest,farthestnum,h,c;
	sint16 		angle,room_number;

	/* Limit target elevation else get horrible camera twists when nearly vertical */
	if (!camera.target_elevation)
		camera.target_elevation = NORMAL_ELEVATION;

	camera.target_elevation += item->pos.x_rot;
	if (camera.target_elevation > MAX_ELEVATION)
		camera.target_elevation = MAX_ELEVATION;
	else if (camera.target_elevation < -MAX_ELEVATION)
		camera.target_elevation = -MAX_ELEVATION;

	distance = camera.target_distance * phd_cos(camera.target_elevation) >> W2V_SHIFT;
	room_number = camera.target.room_number;
	floor = GetFloor(camera.target.x, camera.target.y+256, camera.target.z,&room_number);
	if (room[room_number].flags & SWAMP)
		camera.target.y = room[room_number].y-256;

	floor = GetFloor(camera.target.x, camera.target.y, camera.target.z,&camera.target.room_number);

	h = GetHeight(floor,camera.target.x, camera.target.y, camera.target.z);
	c = GetCeiling(floor,camera.target.x, camera.target.y, camera.target.z);
	if (c+16 > h-16 && h != NO_HEIGHT && c != NO_HEIGHT)
	{
		camera.target.y = (c + h) >> 1;
		camera.target_elevation = 0;
	}
	else if (camera.target.y > h-16 && h != NO_HEIGHT)
	{
		camera.target.y = h-16;
		camera.target_elevation = 0;
	}
	else if (camera.target.y < c+16 && c != NO_HEIGHT)
	{
		camera.target.y = c+16;
		camera.target_elevation = 0;
	}

	floor = GetFloor(camera.target.x, camera.target.y, camera.target.z,&camera.target.room_number);

	{
		sint16	room_number;
		long		wx,wy,wz;

		wx = camera.target.x;
		wy = camera.target.y;
		wz = camera.target.z;
		room_number = camera.target.room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		h=GetHeight(floor,wx,wy,wz);
		c=GetCeiling(floor,wx,wy,wz);
		if (wy < c || wy > h || c>=h || h==NO_HEIGHT || c == NO_HEIGHT)
		{
			camera.target.x = last_target.x;
			camera.target.y = last_target.y;
			camera.target.z = last_target.z;
			camera.target.room_number = last_target.room_number;
//			printf("Camera target fucked! Room: %d X:%d Y:%d Z:%d\n",room_number,wx,wy,wz);

#ifdef CHECK_ILLEGAL_CAMPOS
			wx = camera.target.x;
			wy = camera.target.y;
			wz = camera.target.z;
			room_number = camera.target.room_number;
			floor = GetFloor(wx, wy, wz, &room_number);
			h=GetHeight(floor,wx,wy,wz);
			c=GetCeiling(floor,wx,wy,wz);
			if (wy < c || wy > h || c>=h || h==NO_HEIGHT || c == NO_HEIGHT)
				printf("!!!!!\n");
#endif
		}
	}

	for (lp=0;lp<5;lp++)
		ideals[lp].y = camera.target.y + (camera.target_distance * phd_sin(camera.target_elevation) >> W2V_SHIFT);

	farthest = 0x7fffffff;
	farthestnum = 0;
	/* Place camera according to preset angles */
	for (lp=0;lp<5;lp++)
	{
		if (lp==0)
			angle = item->pos.y_rot + camera.target_angle;
		else
			angle = (lp-1) * 0x4000;

		ideals[lp].x = camera.target.x - ((distance * phd_sin(angle)) >> W2V_SHIFT);
		ideals[lp].z = camera.target.z - ((distance * phd_cos(angle)) >> W2V_SHIFT);
		ideals[lp].room_number = camera.target.room_number;

/*		room_number = ideals[lp].room_number;
		floor = GetFloor(ideals[lp].x, ideals[lp].y, ideals[lp].z,&room_number);
		h = GetHeight(floor,ideals[lp].x, ideals[lp].y, ideals[lp].z);
		c = GetCeiling(floor,ideals[lp].x, ideals[lp].y, ideals[lp].z);
		if (c+256 > h-256 && h != NO_HEIGHT && c != NO_HEIGHT)
			ideals[lp].y = (c + h) >> 1;
		else if (ideals[lp].y > h-256 && h != NO_HEIGHT && ideals[lp].y-h < MAX_HEIGHT_CHANGE)
			ideals[lp].y = h-256;
		else if (ideals[lp].y < c+256 && c != NO_HEIGHT && c-ideals[lp].y < MAX_HEIGHT_CHANGE)
			ideals[lp].y = c+256;
*/
		if (mgLOS(&camera.target,&ideals[lp],MAX_CAMERA_PUSH))
		{
			temp[0].x = ideals[lp].x;
			temp[0].y = ideals[lp].y;
			temp[0].z = ideals[lp].z;
			temp[0].room_number = ideals[lp].room_number;
			temp[1].x = camera.pos.x;
			temp[1].y = camera.pos.y;
			temp[1].z = camera.pos.z;
			temp[1].room_number = camera.pos.room_number;

			if (lp == 0 || mgLOS(&temp[0],&temp[1],0))	// Can the current camera position get to this position ?
			{
				if (lp == 0)
				{
					farthestnum = 0;
					break;
				}

				dx = SQUARE(camera.pos.x - ideals[lp].x);
				dx += SQUARE(camera.pos.z - ideals[lp].z);

				if (dx < farthest)
				{
					farthest = dx;
					farthestnum = lp;
				}
			}
		}
		else if (lp==0)
		{
			temp[0].x = ideals[lp].x;
			temp[0].y = ideals[lp].y;
			temp[0].z = ideals[lp].z;
			temp[0].room_number = ideals[lp].room_number;
			temp[1].x = camera.pos.x;
			temp[1].y = camera.pos.y;
			temp[1].z = camera.pos.z;
			temp[1].room_number = camera.pos.room_number;

			if (lp == 0 || mgLOS(&temp[0],&temp[1],0))	// Can the current camera position get to this position ?
			{
				dx = SQUARE(camera.target.x - ideals[lp].x);
				dz = SQUARE(camera.target.z - ideals[lp].z);

				if ((dx+dz) > SQUARE(768))
				{
					farthestnum = 0;
					break;
				}
			}
		}
	}

	ideal.x = ideals[farthestnum].x;
	ideal.y = ideals[farthestnum].y;
	ideal.z = ideals[farthestnum].z;
	ideal.room_number = ideals[farthestnum].room_number;

	CameraCollisionBounds(&ideal, 384, 1);

	if (camera.old_type == FIXED_CAMERA)
		camera.speed = 1;
	MoveCamera(&ideal, camera.speed);
}

void CombatCamera(ITEM_INFO *item)
{
	/* Standard follow Lara camera */
	FLOOR_INFO	*floor;
	GAME_VECTOR 	ideal;
	GAME_VECTOR	ideals[9];
	GAME_VECTOR	temp[2];
	sint32 		distance,lp,dx,dz,farthest,farthestnum,h,c;
	sint16 		angle,room_number;

	camera.target.z = item->pos.z_pos;
	camera.target.x = item->pos.x_pos;

	/* If Target Selected Look At It Else uses Lara's head position facing */
	if ( lara.target )
	{
		camera.target_angle = item->pos.y_rot + lara.target_angles[0];
		camera.target_elevation = item->pos.x_rot + lara.target_angles[1];
	}
	else
	{
		camera.target_angle = item->pos.y_rot + lara.head_y_rot + lara.torso_y_rot;
		camera.target_elevation = item->pos.x_rot + lara.head_x_rot + lara.torso_x_rot + COMBAT_ELEVATION;
	}

	room_number = camera.target.room_number;
	floor = GetFloor(camera.target.x, camera.target.y+256, camera.target.z,&room_number);
	if (room[room_number].flags & SWAMP)
		camera.target.y = room[room_number].y-256;

	floor = GetFloor(camera.target.x, camera.target.y, camera.target.z,&camera.target.room_number);

	h = GetHeight(floor,camera.target.x, camera.target.y, camera.target.z);
	c = GetCeiling(floor,camera.target.x, camera.target.y, camera.target.z);
	if (c+64 > h-64 && h != NO_HEIGHT && c != NO_HEIGHT)
	{
		camera.target.y = (c + h) >> 1;
		camera.target_elevation = 0;
	}
	else if (camera.target.y > h-64 && h != NO_HEIGHT)
	{
		camera.target.y = h-64;
		camera.target_elevation = 0;
	}
	else if (camera.target.y < c+64 && c != NO_HEIGHT)
	{
		camera.target.y = c+64;
		camera.target_elevation = 0;
	}

	floor = GetFloor(camera.target.x, camera.target.y, camera.target.z,&camera.target.room_number);

	{
		sint16	room_number;
		long		wx,wy,wz;

		wx = camera.target.x;
		wy = camera.target.y;
		wz = camera.target.z;
		room_number = camera.target.room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		h=GetHeight(floor,wx,wy,wz);
		c=GetCeiling(floor,wx,wy,wz);
		if (wy < c || wy > h || c>=h || h==NO_HEIGHT || c == NO_HEIGHT)
		{
			camera.target.x = last_target.x;
			camera.target.y = last_target.y;
			camera.target.z = last_target.z;
			camera.target.room_number = last_target.room_number;
//			printf("Camera target fucked! Room: %d X:%d Y:%d Z:%d\n",room_number,wx,wy,wz);

#ifdef CHECK_ILLEGAL_CAMPOS
			wx = camera.target.x;
			wy = camera.target.y;
			wz = camera.target.z;
			room_number = camera.target.room_number;
			floor = GetFloor(wx, wy, wz, &room_number);
			h=GetHeight(floor,wx,wy,wz);
			c=GetCeiling(floor,wx,wy,wz);
			if (wy < c || wy > h || c>=h || h==NO_HEIGHT || c == NO_HEIGHT)
				printf("!!!!!\n");
#endif
		}
	}

	camera.target_distance = COMBAT_DISTANCE;
	distance = camera.target_distance * phd_cos(camera.target_elevation) >> W2V_SHIFT;

	for (lp=0;lp<5;lp++)
		ideals[lp].y = camera.target.y + (camera.target_distance * phd_sin(camera.target_elevation) >> W2V_SHIFT);

	farthest = 0x7fffffff;
	farthestnum = 0;
	/* Place camera according to preset angles */
	for (lp=0;lp<5;lp++)
	{
		if (lp==0)
			angle = camera.target_angle;
		else
			angle = (lp-1) * 0x4000;

		ideals[lp].x = camera.target.x - ((distance * phd_sin(angle)) >> W2V_SHIFT);
		ideals[lp].z = camera.target.z - ((distance * phd_cos(angle)) >> W2V_SHIFT);
		ideals[lp].room_number = camera.target.room_number;

/*		room_number = ideals[lp].room_number;
		floor = GetFloor(ideals[lp].x, ideals[lp].y, ideals[lp].z,&room_number);
		h = GetHeight(floor,ideals[lp].x, ideals[lp].y, ideals[lp].z);
		c = GetCeiling(floor,ideals[lp].x, ideals[lp].y, ideals[lp].z);
		if (c+256 > h-256 && h != NO_HEIGHT && c != NO_HEIGHT)
			ideals[lp].y = (c + h) >> 1;
		else if (ideals[lp].y > h-256 && h != NO_HEIGHT && ideals[lp].y-h < MAX_HEIGHT_CHANGE)
			ideals[lp].y = h-256;
		else if (ideals[lp].y < c+256 && c != NO_HEIGHT && c-ideals[lp].y < MAX_HEIGHT_CHANGE)
			ideals[lp].y = c+256;
*/
		if (mgLOS(&camera.target,&ideals[lp],MAX_CAMERA_PUSH))
		{
			temp[0].x = ideals[lp].x;
			temp[0].y = ideals[lp].y;
			temp[0].z = ideals[lp].z;
			temp[0].room_number = ideals[lp].room_number;
			temp[1].x = camera.pos.x;
			temp[1].y = camera.pos.y;
			temp[1].z = camera.pos.z;
			temp[1].room_number = camera.pos.room_number;

			if (lp == 0 || mgLOS(&temp[0],&temp[1],0))	// Can the current camera position get to this position ?
			{
				if (lp == 0)
				{
					farthestnum = 0;
					break;
				}

				dx = SQUARE(camera.pos.x - ideals[lp].x);
				dx += SQUARE(camera.pos.z - ideals[lp].z);

				if (dx < farthest)
				{
					farthest = dx;
					farthestnum = lp;
				}
			}
		}
		else if (lp==0)
		{
			temp[0].x = ideals[lp].x;
			temp[0].y = ideals[lp].y;
			temp[0].z = ideals[lp].z;
			temp[0].room_number = ideals[lp].room_number;
			temp[1].x = camera.pos.x;
			temp[1].y = camera.pos.y;
			temp[1].z = camera.pos.z;
			temp[1].room_number = camera.pos.room_number;

			if (lp == 0 || mgLOS(&temp[0],&temp[1],0))	// Can the current camera position get to this position ?
			{
				dx = SQUARE(camera.target.x - ideals[lp].x);
				dz = SQUARE(camera.target.z - ideals[lp].z);

				if ((dx+dz) > SQUARE(768))
				{
					farthestnum = 0;
					break;
				}
			}
		}
	}

	ideal.x = ideals[farthestnum].x;
	ideal.y = ideals[farthestnum].y;
	ideal.z = ideals[farthestnum].z;
	ideal.room_number = ideals[farthestnum].room_number;

	CameraCollisionBounds(&ideal, 384, 1);

	if (camera.old_type == FIXED_CAMERA)
		camera.speed = 1;
	MoveCamera(&ideal, camera.speed);
}

void LookCamera(ITEM_INFO *item)
{
	/* Looks in direction of Lara's head. If LOOK_CAMERA, shifts forwards and backwards
		when look up/down; if COMBAT_CAMERA doesn't do this */
	GAME_VECTOR 	oldt, oldp, ideal;
	PHD_VECTOR	pos1,pos2,pos3;
	FLOOR_INFO 	*floor;
	sint32		shake,lp;
	sint16 		room_number,room_number2;
	sint32 		dx,dy,dz,wx,wy,wz,clipped,h,c,hxrot,txrot,hyrot,tyrot,speed;

// Pos1 = position of head, 2 = position of camera, 3 = position of target.

	hxrot = lara.head_x_rot;
	hyrot = lara.head_y_rot;
	txrot = lara.torso_x_rot;
	tyrot = lara.torso_y_rot;

	lara.torso_x_rot=0;
	lara.torso_y_rot=0;
	lara.head_x_rot <<= 1;
	lara.head_y_rot <<= 1;

	if (lara.head_x_rot > MIN_LOOKX_ROTATION)	// Look up.
		lara.head_x_rot = MIN_LOOKX_ROTATION;
	else if (lara.head_x_rot < MAX_LOOKX_ROTATION)	// Look down.
		lara.head_x_rot = MAX_LOOKX_ROTATION;
	if (lara.head_y_rot < -MAX_LOOKY_ROTATION)
		lara.head_y_rot = -MAX_LOOKY_ROTATION;
	else if (lara.head_y_rot > MAX_LOOKY_ROTATION)
		lara.head_y_rot = MAX_LOOKY_ROTATION;

	pos1.x = 0;
	pos1.y = 16;
	pos1.z = 64;
	GetLaraHandAbsPosition( &pos1, LARA_HEAD );     // Get Position of Head

	clipped = 0;
	room_number = lara_item->room_number;
	floor = GetFloor(pos1.x, pos1.y+256, pos1.z, &room_number);
	if (room[room_number].flags & SWAMP)
	{
		pos1.y = room[room_number].y-256;
		floor = GetFloor(pos1.x, pos1.y, pos1.z, &room_number);
	}
	else
		floor = GetFloor(pos1.x, pos1.y, pos1.z, &room_number);
	h=GetHeight(floor,pos1.x,pos1.y,pos1.z);
	c=GetCeiling(floor,pos1.x,pos1.y,pos1.z);

	if (h == NO_HEIGHT || c == NO_HEIGHT || c>=h || pos1.y > h || pos1.y < c)
		clipped=1;

	if (clipped)
	{
		pos1.x = 0;
		pos1.y = 16;
		pos1.z = 0;
		GetLaraHandAbsPosition( &pos1, LARA_HEAD );     // Get Position of Head

		clipped = 0;
		room_number = lara_item->room_number;
		floor = GetFloor(pos1.x, pos1.y+256, pos1.z, &room_number);
		if (room[room_number].flags & SWAMP)
		{
			pos1.y = room[room_number].y-256;
			floor = GetFloor(pos1.x, pos1.y, pos1.z, &room_number);
		}
		else
			floor = GetFloor(pos1.x, pos1.y, pos1.z, &room_number);
		h=GetHeight(floor,pos1.x,pos1.y,pos1.z);
		c=GetCeiling(floor,pos1.x,pos1.y,pos1.z);

		if (h == NO_HEIGHT || c == NO_HEIGHT || c>=h || pos1.y > h || pos1.y < c)
			clipped=1;

		if (clipped)
		{
			pos1.x = 0;
			pos1.y = 16;
			pos1.z = -64;
			GetLaraHandAbsPosition( &pos1, LARA_HEAD );     // Get Position of Head
		}
	}

	pos2.x = 0;
	pos2.y = 0;
	pos2.z = -1024;
	GetLaraHandAbsPosition( &pos2, LARA_HEAD );     // Get Position of camera.
	pos3.x = 0;
	pos3.y = 0;
	pos3.z = 2048;
	GetLaraHandAbsPosition( &pos3, LARA_HEAD );     // Get Position of target.

	/* Remember old position to smooth camera movement */

	dx = (pos2.x-pos1.x) >> 3;
	dy = (pos2.y-pos1.y) >> 3;
	dz = (pos2.z-pos1.z) >> 3;
	wx = pos1.x;
	wy = pos1.y;
	wz = pos1.z;

	room_number = room_number2 = lara_item->room_number;
	for (lp=0;lp<8;lp++)
	{
		room_number = room_number2;
		floor = GetFloor(wx, wy+256, wz, &room_number2);
		if (room[room_number2].flags & SWAMP)
		{
			wy = room[room_number2].y - 256;
			break;
		}
		else
			floor = GetFloor(wx, wy, wz, &room_number2);
		h = GetHeight(floor,wx,wy,wz);
		c = GetCeiling(floor,wx,wy,wz);
		if (h == NO_HEIGHT || c == NO_HEIGHT || c>=h || wy > h || wy < c)
			break;
		wx+=dx;
		wy+=dy;
		wz+=dz;
	}

	if (lp)
	{
		wx-=dx;
		wy-=dy;
		wz-=dz;
	}

	ideal.x = wx;
	ideal.y = wy;
	ideal.z = wz;
	ideal.room_number = room_number;

	if (	old_cam.pos.x_rot == lara.head_x_rot &&
		old_cam.pos.y_rot == lara.head_y_rot &&
		old_cam.pos.x_pos == lara_item->pos.x_pos &&
		old_cam.pos.y_pos == lara_item->pos.y_pos &&
		old_cam.pos.z_pos == lara_item->pos.z_pos &&
		old_cam.current_anim_state == lara_item->current_anim_state &&
		old_cam.goal_anim_state == lara_item->goal_anim_state &&
		camera.old_type == LOOK_CAMERA)
	{
		ideal.x = static_lookcamp.x;
		ideal.y = static_lookcamp.y;
		ideal.z = static_lookcamp.z;
		ideal.room_number = static_lookcamp.room_number;
		pos3.x = static_lookcamt.x;
		pos3.y = static_lookcamt.y;
		pos3.z = static_lookcamt.z;
	}
	else
	{
		old_cam.pos.x_rot = lara.head_x_rot;
		old_cam.pos.y_rot = lara.head_y_rot;
		old_cam.pos.x_pos = lara_item->pos.x_pos;
		old_cam.pos.y_pos = lara_item->pos.y_pos;
		old_cam.pos.z_pos = lara_item->pos.z_pos;
		old_cam.current_anim_state = lara_item->current_anim_state;
		old_cam.goal_anim_state = lara_item->goal_anim_state;
		static_lookcamp.x = ideal.x;
		static_lookcamp.y = ideal.y;
		static_lookcamp.z = ideal.z;
		static_lookcamp.room_number = ideal.room_number;
		static_lookcamt.x = pos3.x;
		static_lookcamt.y = pos3.y;
		static_lookcamt.z = pos3.z;
	}

	CameraCollisionBounds(&ideal,224, 1);

	if (camera.old_type == FIXED_CAMERA)
	{
		camera.pos.x = ideal.x;
		camera.pos.y = ideal.y;
		camera.pos.z = ideal.z;
		camera.target.x = pos3.x;
		camera.target.y = pos3.y;
		camera.target.z = pos3.z;
		camera.target.room_number = lara_item->room_number;
	}
	else
	{
		camera.pos.x += (ideal.x - camera.pos.x) >> 2;
		camera.pos.y += (ideal.y - camera.pos.y) >> 2;
		camera.pos.z += (ideal.z - camera.pos.z) >> 2;
		camera.target.x += (pos3.x - camera.target.x) >> 2;
		camera.target.y += (pos3.y - camera.target.y) >> 2;
		camera.target.z += (pos3.z - camera.target.z) >> 2;
		camera.target.room_number = lara_item->room_number;
	}

	if (camera.bounce && camera.type == camera.old_type)
	{
		if (camera.bounce > 0)
		{
			camera.pos.y += camera.bounce;
			camera.target.y += camera.bounce;
			camera.bounce = 0;
		}
		else
		{
			/* Shake camera randomly */
			shake = ((GetRandomControl()-0x4000) * camera.bounce) >> 15;
			camera.pos.x += shake;
			camera.target.y += shake;
			shake = ((GetRandomControl()-0x4000) * camera.bounce) >> 15;
			camera.pos.y += shake;
			camera.target.y += shake;
			shake = ((GetRandomControl()-0x4000) * camera.bounce) >> 15;
			camera.pos.z += shake;
			camera.target.z += shake;
			camera.bounce += 5;
		}
	}

	GetFloor(camera.pos.x, camera.pos.y, camera.pos.z, &camera.pos.room_number);

	{
		sint16	room_number;
		long		wx,wy,wz;

		wx = camera.pos.x;
		wy = camera.pos.y;
		wz = camera.pos.z;
		room_number = camera.pos.room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		h=GetHeight(floor,wx,wy,wz);
		c=GetCeiling(floor,wx,wy,wz);

		if (wy-255 < c && wy+255 > h && c<h && c != NO_HEIGHT && h != NO_HEIGHT)
			camera.pos.y = (h+c) >> 1;
		else if (wy+255 > h && c<h && c != NO_HEIGHT && h != NO_HEIGHT)
			camera.pos.y = h-255;
		else if (wy-255 < c && c<h && c != NO_HEIGHT && h != NO_HEIGHT)
			camera.pos.y = c+255;
	}

//	CameraCollisionBounds(&camera.pos,224);

	{
		sint16	room_number;
		long		wx,wy,wz;

		wx = camera.pos.x;
		wy = camera.pos.y;
		wz = camera.pos.z;
		room_number = camera.pos.room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		h=GetHeight(floor,wx,wy,wz);
		c=GetCeiling(floor,wx,wy,wz);
		if ((room[room_number].flags&SWAMP))
			camera.pos.y = room[room_number].y-256;
		else	if (wy < c || wy > h || c>=h || h==NO_HEIGHT || c==NO_HEIGHT)
  			mgLOS(&camera.target,&camera.pos,0);
	}

	{
		sint16	room_number;
		long		wx,wy,wz;

		wx = camera.pos.x;
		wy = camera.pos.y;
		wz = camera.pos.z;
		room_number = camera.pos.room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		h=GetHeight(floor,wx,wy,wz);
		c=GetCeiling(floor,wx,wy,wz);
		if (wy < c || wy > h || c>=h || h==NO_HEIGHT || c==NO_HEIGHT || (room[room_number].flags&SWAMP))
		{
			camera.pos.x = pos1.x;
			camera.pos.y = pos1.y;
			camera.pos.z = pos1.z;
			camera.pos.room_number = lara_item->room_number;
		}
	}

	GetFloor(camera.pos.x,camera.pos.y,camera.pos.z,&camera.pos.room_number);
	phd_LookAt(camera.pos.x, camera.pos.y, camera.pos.z, camera.target.x, camera.target.y, camera.target.z, 0);

	/* For stereo sound effects need to know camera facing */
#ifdef PC_VERSION
	if (camera.mike_at_lara)
	{
		camera.actual_angle=lara_item->pos.y_rot+lara.head_y_rot+lara.torso_y_rot;
		camera.mike_pos.x=lara_item->pos.x_pos;
		camera.mike_pos.y=lara_item->pos.y_pos;
		camera.mike_pos.z=lara_item->pos.z_pos;
	}
	else
#endif
	{
		camera.actual_angle = phd_atan(camera.target.z - camera.pos.z, camera.target.x - camera.pos.x);
		camera.mike_pos.x=camera.pos.x+((phd_persp*phd_sin(camera.actual_angle))>>W2V_SHIFT);
		camera.mike_pos.z=camera.pos.z+((phd_persp*phd_cos(camera.actual_angle))>>W2V_SHIFT);
		camera.mike_pos.y=camera.pos.y;
	}

	camera.old_type = camera.type;

	lara.head_x_rot = hxrot;
	lara.head_y_rot = hyrot;
	lara.torso_x_rot = txrot;
	lara.torso_y_rot = tyrot;

}

static void FixedCamera(void)
{
	/* Move to fixed camera position */
	GAME_VECTOR ideal;
	OBJECT_VECTOR *fixed;

	if (UseForcedFixedCamera)
	{
		ideal.x = ForcedFixedCamera.x;
		ideal.y = ForcedFixedCamera.y;
		ideal.z = ForcedFixedCamera.z;
		ideal.room_number = ForcedFixedCamera.room_number;
	}
	else
	{
		fixed = &camera.fixed[camera.number];
		ideal.x = fixed->x;
		ideal.y = fixed->y;
		ideal.z = fixed->z;
		ideal.room_number = fixed->data;
	}

	/* If something gets between target and fixed camera, need to clip it */
//	if (!LOS(&camera.target, &ideal))
// 		do_something....

	camera.fixed_camera = 1;

	MoveCamera(&ideal, 1);

	/* Deal with timer (if any) */
	if (camera.timer)
	{
		camera.timer--;
		if (!camera.timer)
			camera.timer = -1;
	}
}

void CalculateCamera(void)
{
	/* Calculate the position of the viewpoint (based eventually on all kinds of stuff) */
	ITEM_INFO 	*item;
	OBJECT_INFO *object;
	FLOOR_INFO 	*floor;
	sint16 		*bounds, angle, tilt, change;
	sint32	 	shift, fixed_camera, y;

	oldtx = camera.target.x;
	oldty = camera.target.y;
	oldtz = camera.target.z;

	if (UseForcedFixedCamera)
	{
		camera.type = FIXED_CAMERA;
		camera.speed = 1;
	}

   /* Make nice underWater sound if camera underWater */
  	if (room[camera.pos.room_number].flags & UNDERWATER)
  	{
  		SoundEffect( 60, NULL, SFX_ALWAYS );
		if (!camera.underwater)
		{
			S_CDVolume(0);
			camera.underwater = 1;
		}
  	}
	else
	{
		if (camera.underwater)
		{
	     	//mn_stop_sound_effect(60, NULL);
			//SOUND_Stop();
			if (Option_Music_Volume)
				S_CDVolume(Option_Music_Volume * 25 + 5);
			camera.underwater = 0;
		}
	}

	if (camera.type == CINEMATIC_CAMERA)
 	{
 		InGameCinematicCamera();
 		return;
	}

	/* Alter the behaviour of the GetHeight/Ceiling routines */
	if (camera.flags != NO_CHUNKY)
		chunky_flag = 0;

	/* Target may not be Lara if it has been set and fixed camera in operation */
	fixed_camera = (camera.item && (camera.type == FIXED_CAMERA || camera.type == HEAVY_CAMERA));
	item = fixed_camera ? camera.item : lara_item;

	/* Setup target (which is item's mid point filtered to remove excess shake) Ha! Of course it does. */
	object = &objects[item->object_number];
	bounds = GetBoundsAccurate(item);

	y = item->pos.y_pos;
	if (!fixed_camera)
		y += bounds[3] + ((bounds[2] - bounds[3]) * 3 >> 2);
	else
		y += (bounds[2] + bounds[3])/2;

	/* TARGET_CAMERA: If target but no camera, then make Lara's head turn to look at it */
	if (camera.item && !fixed_camera)
	{
		shift = phd_sqrt(SQUARE(camera.item->pos.z_pos - item->pos.z_pos) + SQUARE(camera.item->pos.x_pos - item->pos.x_pos));
		angle = phd_atan(camera.item->pos.z_pos - item->pos.z_pos, camera.item->pos.x_pos - item->pos.x_pos) - item->pos.y_rot;
		bounds = GetBoundsAccurate(camera.item);
		tilt = phd_atan(shift, y - (camera.item->pos.y_pos + (bounds[2] + bounds[3])/2));
		angle >>= 1;
		tilt >>= 1;
		if (angle > -MAX_HEAD_ROTATION && angle < MAX_HEAD_ROTATION && tilt > MIN_HEAD_TILT && tilt < MAX_HEAD_TILT)
		{
			change = angle - lara.head_y_rot;
			if (change > HEAD_TURN)
				lara.head_y_rot += HEAD_TURN;
			else if (change < -HEAD_TURN)
				lara.head_y_rot -= HEAD_TURN;
			else
				lara.head_y_rot += change;
			lara.torso_y_rot = lara.head_y_rot;

			change = tilt - lara.head_x_rot;
			if (change > HEAD_TURN)
				lara.head_x_rot += HEAD_TURN;
			else if (change < -HEAD_TURN)
				lara.head_x_rot -= HEAD_TURN;
			else
				lara.head_x_rot += change;
			lara.torso_x_rot = lara.head_x_rot;

			camera.type = LOOK_CAMERA;

			/* Flag that this item has been 'spotted' by Lara */
			camera.item->looked_at = 1;
		}
	}

	if (camera.type == LOOK_CAMERA || camera.type == COMBAT_CAMERA)
	{
		if (camera.type == COMBAT_CAMERA)
		{
			last_target.x = camera.target.x;
			last_target.y = camera.target.y;
			last_target.z = camera.target.z;
			last_target.room_number = camera.target.room_number;
		}

		/* To do the midly unpleasant target shift when look up down, need to know old position */
		y -= STEP_L;
		camera.target.room_number = item->room_number;

		/* Don't smooth target shift if coming from FIXED_CAMERA/HEAVY_CAMERA */
		if (camera.fixed_camera)
		{
			camera.target.y = y;
			camera.speed = 1;
		}
		else
		{
			camera.target.y += ((y - camera.target.y) >> 2);
			camera.speed = (camera.type == LOOK_CAMERA)? LOOK_SPEED : COMBAT_SPEED;
		}

		/* Fixed camera allows speed to be set for camera movement (as well as remembering if last
			camera was fixed with non-Lara target) */
		camera.fixed_camera = 0;

		if (camera.type == LOOK_CAMERA)
			LookCamera(item);
		else
			CombatCamera(item);
	}
	else
	{
		/* Shift to follow centre of bounds if required (e.g. Push/PullBlock) */

		last_target.x = camera.target.x;
		last_target.y = camera.target.y;
		last_target.z = camera.target.z;
		last_target.room_number = camera.target.room_number;

		camera.target.x = item->pos.x_pos;
		camera.target.z = item->pos.z_pos;

		if (camera.flags == FOLLOW_CENTRE)
		{
			shift = (bounds[4] + bounds[5])/2;
			camera.target.z += (phd_cos(item->pos.y_rot) * shift) >> W2V_SHIFT;
			camera.target.x += (phd_sin(item->pos.y_rot) * shift) >> W2V_SHIFT;
		}
		camera.target.room_number = item->room_number;

		/* Don't smooth target shift if switching between fixed cameras (on or off) */
		if (camera.fixed_camera ^ fixed_camera)
		{
			camera.target.y = y;
			camera.fixed_camera = 1;
			camera.speed = 1;
		}
		else
		{
//			camera.target.y += (y - camera.target.y)/4;
			camera.target.y = y;
			camera.fixed_camera = 0;
		}

		if (camera.speed != 1 && camera.old_type != LOOK_CAMERA)
		{
			camera.target.x = oldtx + ((camera.target.x - oldtx) >> 2);
			camera.target.y = oldty + ((camera.target.y - oldty) >> 2);
			camera.target.z = oldtz + ((camera.target.z - oldtz) >> 2);
		}

		/* Possible for item's centre point to be shifted into another room */
		floor = GetFloor( camera.target.x, camera.target.y, camera.target.z, &camera.target.room_number);

		/* A target on a steep slope may cause camera to go mad if chunky GetHeight is used */
//		if (camera.target.y > GetHeight(floor, camera.target.x, camera.target.y, camera.target.z))
//			chunky_flag = 0;

		if (camera.type == CHASE_CAMERA || camera.flags == CHASE_OBJECT)
			ChaseCamera(item);
		else
			FixedCamera();
	}

	camera.last = camera.number;
	camera.fixed_camera = fixed_camera;

	if (camera.type != HEAVY_CAMERA || camera.timer == -1)
	{
		/* Reset camera stuff for next time */
		camera.type = CHASE_CAMERA;
		camera.speed = CHASE_SPEED;
		camera.number = NO_CAMERA;
		camera.last_item = camera.item;
		camera.item = NULL;
		camera.target_angle = camera.target_elevation = 0;
		camera.target_distance = WALL_L*3/2;
		camera.flags = 0;
	}

	chunky_flag = 0;
}


long	CameraCollisionBounds(GAME_VECTOR *ideal, long push, long yfirst)
{
	FLOOR_INFO *floor;
	long		wx,wy,wz,h,c;
	sint16 	room_number;

	wx = ideal->x;
	wy = ideal->y;
	wz = ideal->z;

//	if (wy-255 < c && wy+255 > h && abs(wy-c) < 16384)
//		wy = (h+c) >> 1;
//	else if (wy+255 > h && abs(wy-c) < 16384)
//		wy = h-255;
//	else if (wy-255 < c && abs(wy-c) < 16384)
//		wy = c+255;

	if (yfirst)
	{
		room_number = ideal->room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		h=GetHeight(floor,wx,wy,wz);
		c=GetCeiling(floor,wx,wy,wz);

		if (wy-255 < c && wy+255 > h && c<h && c != NO_HEIGHT && h != NO_HEIGHT)
			wy = (h+c) >> 1;
		else if (wy+255 > h && c<h && c != NO_HEIGHT && h != NO_HEIGHT)
			wy = h-255;
		else if (wy-255 < c && c<h && c != NO_HEIGHT && h != NO_HEIGHT)
			wy = c+255;
	}

	room_number = ideal->room_number;
	floor = GetFloor(wx-push, wy, wz, &room_number);
	h=GetHeight(floor,wx-push,wy,wz);
	c=GetCeiling(floor,wx-push,wy,wz);
	if (wy > h || h == NO_HEIGHT || c == NO_HEIGHT || c>=h || wy < c)
		wx = (wx&(~1023))+push;

	room_number = ideal->room_number;
	floor = GetFloor(wx, wy, wz-push, &room_number);
	h=GetHeight(floor,wx,wy,wz-push);
	c=GetCeiling(floor,wx,wy,wz-push);
	if (wy > h || h == NO_HEIGHT || c == NO_HEIGHT || c>=h || wy < c)
		wz = (wz&(~1023))+push;

	room_number = ideal->room_number;
	floor = GetFloor(wx+push, wy, wz, &room_number);
	h=GetHeight(floor,wx+push,wy,wz);
	c=GetCeiling(floor,wx+push,wy,wz);
	if (wy > h || h == NO_HEIGHT || c == NO_HEIGHT || c>=h || wy < c)
		wx = (wx|1023)-push;

	room_number = ideal->room_number;
	floor = GetFloor(wx, wy, wz+push, &room_number);
	h=GetHeight(floor,wx,wy,wz+push);
	c=GetCeiling(floor,wx,wy,wz+push);
	if (wy > h || h == NO_HEIGHT || c == NO_HEIGHT || c>=h || wy < c)
		wz = (wz|1023)-push;

	if (!yfirst)
	{
		room_number = ideal->room_number;
		floor = GetFloor(wx, wy, wz, &room_number);
		h=GetHeight(floor,wx,wy,wz);
		c=GetCeiling(floor,wx,wy,wz);

		if (wy-255 < c && wy+255 > h && c<h && c != NO_HEIGHT && h != NO_HEIGHT)
			wy = (h+c) >> 1;
		else if (wy+255 > h && c<h && c != NO_HEIGHT && h != NO_HEIGHT)
			wy = h-255;
		else if (wy-255 < c && c<h && c != NO_HEIGHT && h != NO_HEIGHT)
			wy = c+255;
	}

	room_number = ideal->room_number;
	floor = GetFloor(wx, wy, wz, &room_number);
	h=GetHeight(floor,wx,wy,wz);
	c=GetCeiling(floor,wx,wy,wz);
	if (wy > h || wy < c || h == NO_HEIGHT || c == NO_HEIGHT || c>=h)
		return(1);     	// Illegal position.

	floor = GetFloor(wx, wy, wz, &ideal->room_number);
	ideal->x = wx;
	ideal->y = wy;
	ideal->z = wz;

	return (0);
}

long mgLOS(GAME_VECTOR *start, GAME_VECTOR *target, long push)
{
	FLOOR_INFO *floor;
	long		x,y,z,h,c,cdiff,hdiff;
	long		dx,dy,dz,lp,clipped,nc;
	sint16 	room_number,room_number2;

	dx = (target->x - start->x)>>3;
	dy = (target->y - start->y)>>3;
	dz = (target->z - start->z)>>3;

	x = start->x;
	y = start->y;
	z = start->z;
	room_number = room_number2 = start->room_number;

	clipped = nc = 0;
	for (lp=0;lp<8;lp++)
	{
		room_number = room_number2;
		floor = GetFloor(x, y, z, &room_number2);

		if (room[room_number2].flags & SWAMP)
		{
			clipped = 1;
			break;
		}

		h = GetHeight(floor, x, y, z);
		c = GetCeiling(floor, x, y, z);

		if (h == NO_HEIGHT || c == NO_HEIGHT || c>=h)
		{
			if (!nc)
			{
				x += dx;
				y += dy;
				z += dz;
				continue;
			}
			clipped = 1;
			break;
		}

		if (y > h)
		{
			hdiff = y-h;
			if (hdiff < push)
				y = h;
			else
			{
 				clipped = 1;
 				break;
			}
		}

		if (y < c)
		{
			cdiff = c-y;
			if (cdiff < push)
				y = c;
			else
			{
 				clipped = 1;
 				break;
			}
		}

		nc = 1;

		x += dx;
		y += dy;
		z += dz;
	}

	if (lp)
	{
		x -= dx;
		y -= dy;
		z -= dz;
	}

	target->x = x;
	target->y = y;
	target->z = z;

	GetFloor(target->x, target->y, target->z, &room_number);
	target->room_number = room_number;

	return (!clipped);
}
