/***********************************************************************************************************/
/*                                                                                                         */
/* Boat Stuff                                                                               G.Rummery 1997 */
/*                                                                                                         */
/***********************************************************************************************************/

#include "game.h"

enum boat_anims {BOAT_GETON, BOAT_STILL, BOAT_MOVING, BOAT_JUMPR, BOAT_JUMPL, BOAT_HIT, BOAT_FALL, BOAT_NOTUSED, BOAT_DEATH};

#define BOAT_GETONLW_ANIM 0
#define BOAT_GETONRW_ANIM 8
#define BOAT_GETONJ_ANIM  6
#define BOAT_GETON_START  1
#define BOAT_FALL_ANIM	  15
#define BOAT_DEATH_ANIM   18

#define BOAT_UNDO_TURN	(ONE_DEGREE/4)
#define BOAT_TURN			(ONE_DEGREE/8)
#define BOAT_MAX_TURN	(ONE_DEGREE*4)

#define BOAT_MAX_SPEED		90
#define BOAT_SLOW_SPEED		(BOAT_MAX_SPEED/3)
#define BOAT_FAST_SPEED		(BOAT_MAX_SPEED+50)
#define BOAT_MIN_SPEED		20
#define BOAT_ACCELERATION	5
#define BOAT_BRAKE			5
#define BOAT_SLOWDOWN		1
#define BOAT_REVERSE			-5
#define BOAT_MAX_BACK		-20

#define BOAT_MAX_KICK		-80

#define BOAT_SLIP 10
#define BOAT_SIDE_SLIP 30

#define BOAT_FRONT 750
#define BOAT_SIDE	300
#define BOAT_RADIUS 500
#define BOAT_SNOW 500

#define BOAT_MAX_HEIGHT (STEP_L)

#define BOAT_GETOFF_DIST (STEP_L*2)

#define BOAT_WAKE 700
#define BOAT_SOUND_CEILING (WALL_L*5)

// prototypes from skidoo
int DoShift(ITEM_INFO *skidoo, PHD_VECTOR *pos, PHD_VECTOR *old);
int GetCollisionAnim(ITEM_INFO *skidoo, PHD_VECTOR *moved);

#ifdef PSX_VERSION
	ITEM_INFO *boatitem;
	int lara_inboat = 0;
#endif
/************************************ FUNCTION CODE ********************************************************/

void InitialiseBoat(sint16 item_number)
{
	ITEM_INFO *boat;
	BOAT_INFO *binfo;

	/* Allocate extra data to store boat specific information */
	boat = &items[item_number];
	binfo = (BOAT_INFO *)boat->data = game_malloc(sizeof(BOAT_INFO), 0);
	binfo->boat_turn = 0;
	binfo->left_fallspeed = binfo->right_fallspeed = 0;
	binfo->tilt_angle = 0;
	binfo->extra_rotation = 0;
	binfo->water = 0;
	binfo->pitch = 0;
#ifdef PSX_VERSION
	lara_inboat = 0;
#endif
}


int BoatCheckGeton(sint16 item_number, COLL_INFO *coll)
{
	/* Returns 0 if no get on, 1 if right get on and 2 if left get on and 3 if jump geton */
	int geton, dist;
	sint16 rot;
	ITEM_INFO *boat;

	if (lara.gun_status!=LG_ARMLESS)
		return 0;

	boat = &items[item_number];

	dist = ((lara_item->pos.z_pos - boat->pos.z_pos) * phd_cos(-boat->pos.y_rot) -
			  (lara_item->pos.x_pos - boat->pos.x_pos) * phd_sin(-boat->pos.y_rot)) >> W2V_SHIFT;
	if (dist > 200)
		return 0;

	rot = boat->pos.y_rot - lara_item->pos.y_rot;

	/* Check if Lara is close enough and in right position to get onto boat */
	geton = 0;
	if (lara.water_status == LARA_SURFACE || lara.water_status == LARA_WADE)
	{
		if (!(input & IN_ACTION) || lara_item->gravity_status || boat->speed)
			return 0;

		if (rot > 0x2000 && rot < 0x6000)
			geton = 1; //right
		else if (rot > -0x6000 && rot < -0x2000)
			geton = 2; //left
	}
	else if (lara.water_status == LARA_ABOVEWATER)
	{
		if (lara_item->fallspeed > 0)
		{
			if (rot > -0x6000 && rot < 0x6000 && lara_item->pos.y_pos > boat->pos.y_pos)
				geton = 3; //jump
		}
		else if (lara_item->fallspeed == 0)
		{
			if (rot > -0x6000 && rot < 0x6000)
			{
				if (lara_item->pos.x_pos == boat->pos.x_pos && lara_item->pos.y_pos == boat->pos.y_pos && lara_item->pos.z_pos == boat->pos.z_pos)
					geton = 4; // must have started on same spot as boat
				else
					geton = 3; //jump
			}
		}
	}

	if (!geton)
		return 0;

	/* Is Lara actually close enough to get on the thing? */
	if (!TestBoundsCollide(boat, lara_item, coll->radius))
		return 0;

	if (!TestCollision(boat, lara_item))
		return 0;

	return geton;
}


void BoatCollision(sint16 item_number, ITEM_INFO *litem, COLL_INFO *coll)
{
	/* This routine is only for when Lara is not on the boat and she would like to be */
	int geton;
	ITEM_INFO *item;

	/* If Lara dead or already on the boat, then no collision */
	if (litem->hit_points < 0 || lara.skidoo != NO_ITEM)
		return;

	/* If player isn't pressing control or Lara is busy, then do normal object collision */
	geton = BoatCheckGeton(item_number, coll);
	if (!geton)
	{
		coll->enable_baddie_push = 1;
		ObjectCollision(item_number, litem, coll);
		return;
	}

	/* Yeeha! Get in that boat girly */
	lara.skidoo = item_number;

	if (geton == 2)
		litem->anim_number = objects[BOAT_ANIM].anim_index + BOAT_GETONLW_ANIM;
	else if (geton == 1)
		litem->anim_number = objects[BOAT_ANIM].anim_index + BOAT_GETONRW_ANIM;
	else if (geton == 3)
		litem->anim_number = objects[BOAT_ANIM].anim_index + BOAT_GETONJ_ANIM;
	else
		litem->anim_number = objects[BOAT_ANIM].anim_index + BOAT_GETON_START;

	lara.water_status = LARA_ABOVEWATER;
	item = &items[item_number];
	litem->pos.x_pos = item->pos.x_pos;
	litem->pos.y_pos = item->pos.y_pos-5;
	litem->pos.z_pos = item->pos.z_pos;
	litem->pos.y_rot = item->pos.y_rot;
	litem->pos.x_rot = litem->pos.z_rot = 0;
	litem->gravity_status = 0;
	litem->speed = 0;
	litem->fallspeed = 0;
	litem->frame_number = anims[litem->anim_number].frame_base;
	litem->current_anim_state = litem->goal_anim_state = BOAT_GETON;

	// BUG FIX 25/10/97: Sony bug
	if (litem->room_number != item->room_number)
		ItemNewRoom(lara.item_number, item->room_number);

	AnimateItem(litem);

	/* Add to active item list from this point onward */
	if (items[item_number].status != ACTIVE)
	{
		AddActiveItem(item_number);
		items[item_number].status = ACTIVE;
	}
}


sint32 TestWaterHeight(ITEM_INFO *item, sint32 z_off, sint32 x_off, PHD_VECTOR *pos)
{
	/* Get water height at a position offset from the origin.
		Moves the vector in 'pos' to the required test position too */
	FLOOR_INFO *floor;
	sint32 s, c, height;
	sint16 room_number;

	/* Get y pos correctly, but don't bother changing z_off and x_off using x_rot and z_rot */
	pos->y = item->pos.y_pos -	(z_off * phd_sin(item->pos.x_rot) >> W2V_SHIFT) +
		(x_off * phd_sin(item->pos.z_rot) >> W2V_SHIFT);

	c = phd_cos(item->pos.y_rot);
	s = phd_sin(item->pos.y_rot);

	pos->z = item->pos.z_pos + ((z_off * c - x_off * s) >> W2V_SHIFT);
	pos->x = item->pos.x_pos + ((z_off * s + x_off * c) >> W2V_SHIFT);

	/* Try to get water height; if none get ground height instead */
	room_number = item->room_number;
	GetFloor(pos->x, pos->y, pos->z, &room_number); // get correct room (as GetWaterHeight doesn't)
	height = GetWaterHeight(pos->x, pos->y, pos->z, room_number);
	if (height == NO_HEIGHT)
	{
		floor = GetFloor(pos->x, pos->y, pos->z, &room_number);
		height = GetHeight(floor, pos->x, pos->y, pos->z);
		if (height == NO_HEIGHT)
			return height;
	}

	return height - 5; // make sure boat is above water line else all sorts of weirdness results
}

// if we want Boat to kill people
void SkidooBaddieCollision(ITEM_INFO *skidoo);

void DoBoatShift(int boat_number)
{
	ITEM_INFO *item, *boat;
	int item_number, distance, x, z, ix, iz;

	boat = &items[boat_number];

	/* Check if hit something in the water */
	item_number = room[boat->room_number].item_number;
	while (item_number != NO_ITEM)
	{
		item = &items[item_number];

		if (item->object_number == MOTORBOAT && item_number != boat_number && lara.skidoo != item_number)
		{
			// other boat
			x = item->pos.x_pos - boat->pos.x_pos;
			z = item->pos.z_pos - boat->pos.z_pos;
			distance = x*x + z*z;
			if (distance < SQUARE(BOAT_RADIUS*2))
			{
				boat->pos.x_pos = item->pos.x_pos - x * SQUARE(BOAT_RADIUS*2) / distance;
				boat->pos.z_pos = item->pos.z_pos - z * SQUARE(BOAT_RADIUS*2) / distance;
			}
			return;
		}

		item_number = item->next_item;
	}
}


void DoWakeEffect(ITEM_INFO *boat)
{
	int i;
	sint32 c, s, w, h;
	sint16 fx_number, frame;
	FX_INFO *fx;

	*(phd_mxptr+M23) = 0; // no depthQ
	S_CalculateLight(boat->pos.x_pos, boat->pos.y_pos, boat->pos.z_pos, boat->room_number, &boat->il);
	frame = GetRandomDraw() * objects[WATER_SPRITE].nmeshes >> 15;

	for (i=0; i<3; i++)
	{
		fx_number = CreateEffect(boat->room_number);

		if (fx_number != NO_ITEM)
		{
			fx = &effects[fx_number];
			c = phd_cos(boat->pos.y_rot);
			s = phd_sin(boat->pos.y_rot);
			h = BOAT_WAKE;
			w = (1-i) * BOAT_SIDE;
			fx->pos.x_pos = boat->pos.x_pos + ((-(c * w) - (h * s)) >> W2V_SHIFT);
			fx->pos.y_pos = boat->pos.y_pos;
			fx->pos.z_pos = boat->pos.z_pos + (((s * w) - (h * c)) >> W2V_SHIFT);
			fx->pos.y_rot = boat->pos.y_rot - (1-i)*0x4000;
			fx->room_number = boat->room_number;
			fx->frame_number = frame;
			fx->counter = 20;
			fx->object_number = WATER_SPRITE;
			fx->speed = boat->speed>>2;

			if (boat->speed < 64)
				fx->fallspeed = GetRandomDraw() * (ABS(boat->speed)-64) >> 15;
			else
				fx->fallspeed = 0;
			fx->shade = ls_adder - 0x300;
			if (fx->shade < 0)
				fx->shade = 0;
		}
	}
}


int DoBoatDynamics(int height, int fallspeed, int *y)
{
	if (height > *y)
	{
		/* In air */
		*y += fallspeed;
		if (*y > height)
		{
			*y = height;
			fallspeed = 0;
		}
		else
			fallspeed += GRAVITY;
	}
	else
	{
		/* On ground: get up push from height change (if not a closed door and so NO_HEIGHT) */
		fallspeed += ((height - *y - fallspeed) >> 3);
		if (fallspeed < -20)
			fallspeed = -20;

		if (*y > height)
			*y = height;
	}

	return fallspeed;
}


#define BOAT_TIP (BOAT_FRONT+250)

int BoatDynamics(sint16 boat_number)
{
	ITEM_INFO *boat;
	BOAT_INFO *binfo;
	PHD_VECTOR moved, fl, fr, br, bl, f;
	PHD_VECTOR old, fl_old, fr_old, bl_old, br_old, f_old;
	sint32 hfl, hfr, hbr, hbl, hf;
	sint32 hfr_old, hfl_old, hbr_old, hbl_old, hf_old;
	FLOOR_INFO *floor;
	sint32 height, slip, collide;
	sint16 room_number, rot;
	int newspeed;

	boat = &items[boat_number];
	binfo = (BOAT_INFO *)boat->data;

	/* Remove tilt angle (and add again at the end) as effects control */
	boat->pos.z_rot -= binfo->tilt_angle;

	/* First get positions and heights of boat's corners + centre */
	hfl_old = TestWaterHeight(boat, BOAT_FRONT, -BOAT_SIDE, &fl_old);
	hfr_old = TestWaterHeight(boat, BOAT_FRONT, BOAT_SIDE, &fr_old);
	hbl_old = TestWaterHeight(boat, -BOAT_FRONT, -BOAT_SIDE, &bl_old);
	hbr_old = TestWaterHeight(boat, -BOAT_FRONT, BOAT_SIDE, &br_old);
	hf_old = TestWaterHeight(boat, BOAT_TIP, 0, &f_old);
	old.x = boat->pos.x_pos;
	old.y = boat->pos.y_pos;
	old.z = boat->pos.z_pos;

	/* Back left/right may be slightly below ground, so correct for this */
	if (bl_old.y > hbl_old)
		bl_old.y = hbl_old;
	if (br_old.y > hbr_old)
		br_old.y = hbr_old;
	if (fl_old.y > hfl_old)
		fl_old.y = hfl_old;
	if (fr_old.y > hfr_old)
		fr_old.y = hfr_old;
	if (f_old.y > hf_old)
		f_old.y = hf_old;

	boat->pos.y_rot += binfo->boat_turn + binfo->extra_rotation;
	binfo->tilt_angle = binfo->boat_turn * 6;

	/* Move boat according to speed */
	boat->pos.z_pos += boat->speed * phd_cos(boat->pos.y_rot) >> W2V_SHIFT;
	boat->pos.x_pos += boat->speed * phd_sin(boat->pos.y_rot) >> W2V_SHIFT;

	/* Slide boat according to tilts (to avoid getting stuck on slopes) */
	slip = BOAT_SIDE_SLIP * phd_sin(boat->pos.z_rot) >> W2V_SHIFT;
	if (!slip && boat->pos.z_rot)
		slip = (boat->pos.z_rot>0)? 1 : -1;
	boat->pos.z_pos -= slip * phd_sin(boat->pos.y_rot) >> W2V_SHIFT;
	boat->pos.x_pos += slip * phd_cos(boat->pos.y_rot) >> W2V_SHIFT;

	slip = BOAT_SLIP * phd_sin(boat->pos.x_rot) >> W2V_SHIFT;
	if (!slip && boat->pos.x_rot)
		slip = (boat->pos.x_rot>0)? 1 : -1;
	boat->pos.z_pos -= slip * phd_cos(boat->pos.y_rot) >> W2V_SHIFT;
	boat->pos.x_pos -= slip * phd_sin(boat->pos.y_rot) >> W2V_SHIFT;

	/* Remember desired position in case of collisions moving us about */
	moved.x = boat->pos.x_pos;
	moved.z = boat->pos.z_pos;

	/* Collision with other boat? */
	DoBoatShift(boat_number);

	/* Test new positions of points (one at a time) and shift boat accordingly */
	rot = 0;
	hbl = TestWaterHeight(boat, -BOAT_FRONT, -BOAT_SIDE, &bl);
	if (hbl < bl_old.y - STEP_L/2)
		rot = DoShift(boat, &bl, &bl_old);

	hbr = TestWaterHeight(boat, -BOAT_FRONT, BOAT_SIDE, &br);
	if (hbr < br_old.y - STEP_L/2)
		rot += DoShift(boat, &br, &br_old);

	hfl = TestWaterHeight(boat, BOAT_FRONT, -BOAT_SIDE, &fl);
	if (hfl < fl_old.y - STEP_L/2)
		rot += DoShift(boat, &fl, &fl_old);

	hfr = TestWaterHeight(boat, BOAT_FRONT, BOAT_SIDE, &fr);
	if (hfr < fr_old.y - STEP_L/2)
		rot += DoShift(boat, &fr, &fr_old);

	if (!slip)
	{
		hf = TestWaterHeight(boat, BOAT_TIP, 0, &f);
		if (hf < f_old.y - STEP_L/2)
			DoShift(boat, &f, &f_old);
	}

	room_number = boat->room_number;
	floor = GetFloor(boat->pos.x_pos, boat->pos.y_pos, boat->pos.z_pos, &room_number);
	height = GetWaterHeight(boat->pos.x_pos, boat->pos.y_pos, boat->pos.z_pos, room_number);
	if (height == NO_HEIGHT)
		height = GetHeight(floor, boat->pos.x_pos, boat->pos.y_pos, boat->pos.z_pos);
	if (height < boat->pos.y_pos - STEP_L/2)
		DoShift(boat, (PHD_VECTOR *)&boat->pos, &old);

	binfo->extra_rotation = rot;

	/* Get collision anim if boat has been moved from desired position by collisions */
	collide = GetCollisionAnim(boat, &moved);

	/* Check final movement if slipped or collided and adjust speed */
	if (slip || collide)
	{
		newspeed = (boat->pos.z_pos - old.z) * phd_cos(boat->pos.y_rot) + (boat->pos.x_pos - old.x) * phd_sin(boat->pos.y_rot) >> W2V_SHIFT;
		if (lara.skidoo == boat_number && boat->speed > BOAT_MAX_SPEED+BOAT_ACCELERATION && newspeed < boat->speed-10)
		{
			lara_item->hit_points -= (boat->speed - newspeed)>>1;
			lara_item->hit_status = 1;
			SoundEffect(31, &lara_item->pos, 0);
		}

		/* Adjust speed if serious change */
		if (slip)
		{
			/* Only if slip is above certain amount, and boat is not in FAST speed range */
			if (boat->speed <= BOAT_MAX_SPEED+10)
				boat->speed = newspeed;
		}
		else
		{
			if (boat->speed > 0 && newspeed < boat->speed)
				boat->speed = newspeed;
			else if (boat->speed < 0 && newspeed > boat->speed)
				boat->speed = newspeed;
		}

		if (boat->speed < BOAT_MAX_BACK)
			boat->speed = BOAT_MAX_BACK;
	}

	return collide;
}


int BoatUserControl(ITEM_INFO *boat)
{
	/* Return whether to straighten up or not */
	int no_turn=1, max_speed;
	BOAT_INFO *binfo;

	binfo = (BOAT_INFO *)boat->data;

	if (boat->pos.y_pos >= binfo->water - STEP_L/2 && binfo->water!=NO_HEIGHT)
	{
		/* If on the water surface, user has control; allow for reversing! */
		if (!(input & IN_JUMP) && !(input & IN_LOOK))
		{
			if ( ((input & IN_LEFT) && !(input & IN_BACK)) || ((input & IN_RIGHT) && (input & IN_BACK)) )
//			if (input & IN_LEFT)
			{
				if (binfo->boat_turn > 0)
					binfo->boat_turn -= BOAT_UNDO_TURN;
				else
				{
					binfo->boat_turn -= BOAT_TURN;
					if (binfo->boat_turn < -BOAT_MAX_TURN)
						binfo->boat_turn = -BOAT_MAX_TURN;
				}
				no_turn = 0;
			}
			else if ( ((input & IN_RIGHT) && !(input & IN_BACK)) || ((input & IN_LEFT) && (input & IN_BACK)) )
//			else if (input & IN_RIGHT)
			{
				if (binfo->boat_turn < 0)
					binfo->boat_turn += BOAT_UNDO_TURN;
				else
				{
					binfo->boat_turn += BOAT_TURN;
					if (binfo->boat_turn > BOAT_MAX_TURN)
						binfo->boat_turn = BOAT_MAX_TURN;
				}
				no_turn = 0;
			}

			if (input & IN_BACK)
			{
				if (boat->speed > 0)
					boat->speed -= BOAT_BRAKE;
				else if (boat->speed > BOAT_MAX_BACK)
					boat->speed += BOAT_REVERSE;
			}
			else if (input & IN_FORWARD)
			{
				if (input & IN_ACTION)
					max_speed = BOAT_FAST_SPEED;
				else
					max_speed = (input & IN_SLOW)? BOAT_SLOW_SPEED : BOAT_MAX_SPEED;

				if (boat->speed < max_speed)
					boat->speed += BOAT_ACCELERATION/2 + BOAT_ACCELERATION * boat->speed / (2*max_speed);
				else if (boat->speed > max_speed + BOAT_SLOWDOWN)
					boat->speed -= BOAT_SLOWDOWN;
			}
			else if (boat->speed>=0 && boat->speed<BOAT_MIN_SPEED && (input & (IN_LEFT|IN_RIGHT)))
				boat->speed = BOAT_MIN_SPEED; // If user wants to turn, boat will move forward
			else if (boat->speed > BOAT_SLOWDOWN)
				boat->speed -= BOAT_SLOWDOWN;
			else
				boat->speed = 0;
		}
		else if ((input & IN_LOOK) && boat->speed == 0)
			LookUpDown();
	}

	return no_turn;
}


void BoatAnimation(ITEM_INFO *boat, int collide)
{
	BOAT_INFO *binfo;

	binfo = (BOAT_INFO *)boat->data;

	/* Do animation stuff */
	if (lara_item->hit_points <= 0)
	{
		if (lara_item->current_anim_state!=BOAT_DEATH)
		{
			lara_item->anim_number = objects[BOAT_ANIM].anim_index + BOAT_DEATH_ANIM;
			lara_item->frame_number = anims[lara_item->anim_number].frame_base;
			lara_item->current_anim_state = lara_item->goal_anim_state = BOAT_DEATH;
		}
	}
	else if (boat->pos.y_pos < binfo->water-STEP_L/2 && boat->fallspeed > 0)
	{
		if (lara_item->current_anim_state!=BOAT_FALL)
		{
			lara_item->anim_number = objects[BOAT_ANIM].anim_index + BOAT_FALL_ANIM;
			lara_item->frame_number = anims[lara_item->anim_number].frame_base;
			lara_item->current_anim_state = lara_item->goal_anim_state = BOAT_FALL;
		}
	}
	else if (collide)
	{
		if (lara_item->current_anim_state != BOAT_HIT)
		{
			lara_item->anim_number = (sint16)(objects[BOAT_ANIM].anim_index + collide);
			lara_item->frame_number = anims[lara_item->anim_number].frame_base;
			lara_item->current_anim_state = lara_item->goal_anim_state = BOAT_HIT;
		}
	}
	else
	{
		switch (lara_item->current_anim_state)
		{
		case BOAT_STILL:
			if (input & IN_JUMP)
			{
				if (input & IN_RIGHT)
					lara_item->goal_anim_state = BOAT_JUMPR;
				else if (input & IN_LEFT)
					lara_item->goal_anim_state = BOAT_JUMPL;
			}
			if (boat->speed > 0)
				lara_item->goal_anim_state = BOAT_MOVING;
			break;

		case BOAT_MOVING:
			if (input & IN_JUMP)
			{
				if (input & IN_RIGHT)
					lara_item->goal_anim_state = BOAT_JUMPR;
				else if (input & IN_LEFT)
					lara_item->goal_anim_state = BOAT_JUMPL;
			}
			else if (boat->speed <= 0)
				lara_item->goal_anim_state = BOAT_STILL;
			break;

		case BOAT_FALL:
			lara_item->goal_anim_state = BOAT_MOVING;
			break;
		}
	}
}


void BoatControl(sint16 item_number)
{
	ITEM_INFO *boat;
	BOAT_INFO *binfo;
	PHD_VECTOR fl, fr;
	sint32 hfl, hfr, no_turn=1, drive=0;
	FLOOR_INFO *floor;
	sint32 height, collide, water, ceiling, pitch;
	sint16 room_number, x_rot, z_rot;

	boat = &items[item_number];
#ifdef PSX_VERSION
	boatitem = boat;	//Part of major FRIG to fix lonely boat exit
#endif
	binfo = (BOAT_INFO *)boat->data;

	collide = BoatDynamics(item_number);

	/* Now got final position, so get heights under middle and corners (will only have changed
		from above if collision occurred, but recalc anyway as hardly big maths) */
	hfl = TestWaterHeight(boat, BOAT_FRONT, -BOAT_SIDE, &fl);
	hfr = TestWaterHeight(boat, BOAT_FRONT, BOAT_SIDE, &fr);

	room_number = boat->room_number;
	floor = GetFloor(boat->pos.x_pos, boat->pos.y_pos, boat->pos.z_pos, &room_number);
	height = GetHeight(floor, boat->pos.x_pos, boat->pos.y_pos, boat->pos.z_pos);
	ceiling = GetCeiling(floor, boat->pos.x_pos, boat->pos.y_pos, boat->pos.z_pos);
	if (lara.skidoo == item_number)
	{
		TestTriggers(trigger_index, 0);
		TestTriggers(trigger_index, 1); // HEAVY too
	}

	binfo->water = water = GetWaterHeight(boat->pos.x_pos, boat->pos.y_pos, boat->pos.z_pos, room_number);

	/* Deal with user input; will effect next frame, but relies on info calced this frame */
	if (lara.skidoo == item_number && lara_item->hit_points > 0)
	{
		switch (lara_item->current_anim_state)
		{
		case BOAT_GETON:
		case BOAT_JUMPR:
		case BOAT_JUMPL:
			break;

		default:
			drive = 1;
			no_turn = BoatUserControl(boat);
		}
	}
	else
	{
		/* Boat has no driver */
		if (boat->speed > BOAT_SLOWDOWN)
			boat->speed -= BOAT_SLOWDOWN;
		else
			boat->speed = 0;
	}

	if (no_turn)
	{
		/* Straighten up if not under control */
		if (binfo->boat_turn < -BOAT_UNDO_TURN)
			binfo->boat_turn += BOAT_UNDO_TURN;
		else if (binfo->boat_turn > BOAT_UNDO_TURN)
			binfo->boat_turn -= BOAT_UNDO_TURN;
		else
			binfo->boat_turn = 0;
	}


	/* Get floor height (keep slightly above water surface) */
	boat->floor = height - 5;
	if (binfo->water == NO_HEIGHT)
		binfo->water = height;
	else
		binfo->water -= 5;

	/* Do fallspeed effects on boat */
	binfo->left_fallspeed = DoBoatDynamics(hfl, binfo->left_fallspeed, &fl.y);
	binfo->right_fallspeed = DoBoatDynamics(hfr, binfo->right_fallspeed, &fr.y);
	boat->fallspeed = DoBoatDynamics(binfo->water, boat->fallspeed, &boat->pos.y_pos);

	/* Rotate boat to match these heights */
	height = fl.y + fr.y >> 1;
	x_rot = phd_atan(BOAT_FRONT, boat->pos.y_pos - height);
	z_rot = phd_atan(BOAT_SIDE, height - fl.y);

	boat->pos.x_rot += x_rot - boat->pos.x_rot >> 1;
	boat->pos.z_rot += z_rot - boat->pos.z_rot >> 1;

	/* Auto level the boat on flat water (to stop evil shifts) */
	if (!x_rot && ABS(boat->pos.x_rot) < 4)
		boat->pos.x_rot = 0;
	if (!z_rot && ABS(boat->pos.z_rot) < 4)
		boat->pos.z_rot = 0;

	/* If Lara is on the boat, do all her animation and move her to same position as boat, etc */
	if (lara.skidoo == item_number)
	{
		BoatAnimation(boat, collide);

		if (room_number != boat->room_number)
		{
			ItemNewRoom(item_number, room_number);
			ItemNewRoom(lara.item_number, room_number);
		}

		/* Move Lara to the boat position */
		boat->pos.z_rot += binfo->tilt_angle;
		lara_item->pos.x_pos = boat->pos.x_pos;
		lara_item->pos.y_pos = boat->pos.y_pos;
		lara_item->pos.z_pos = boat->pos.z_pos;
		lara_item->pos.x_rot = boat->pos.x_rot;
		lara_item->pos.y_rot = boat->pos.y_rot;
		lara_item->pos.z_rot = boat->pos.z_rot;

		AnimateItem(lara_item);

		/* Set boat on the exact same anim frame */
		if (lara_item->hit_points > 0)
		{
			boat->anim_number = objects[MOTORBOAT].anim_index + (lara_item->anim_number - objects[BOAT_ANIM].anim_index);
			boat->frame_number = anims[boat->anim_number].frame_base + (lara_item->frame_number - anims[lara_item->anim_number].frame_base);
		}

		/* Set camera */
		camera.target_elevation = -20*ONE_DEGREE;
		camera.target_distance = WALL_L*2;

#ifdef PSX_VERSION
		lara_inboat = 1;
#endif
	}
	else
	{
		/* If Lara isn't in the boat, then just move the boat */
		if (room_number != boat->room_number)
			ItemNewRoom(item_number, room_number);
		boat->pos.z_rot += binfo->tilt_angle;
#ifdef PSX_VERSION
		lara_inboat = 0;
#endif
	}


	/* Do sound effect */
	ceiling = water - ceiling;
	if (ceiling < BOAT_SOUND_CEILING)
		pitch = boat->speed * ceiling / BOAT_SOUND_CEILING;
	else
		pitch = boat->speed;
	binfo->pitch += (pitch - binfo->pitch) >> 2;

	if (boat->speed && water-5 != boat->pos.y_pos)
		SoundEffect(336, &boat->pos, 0);
	else if (boat->speed > 20)
		SoundEffect(197, &boat->pos, PITCH_SHIFT + (0x10000 - (BOAT_MAX_SPEED - binfo->pitch)*100 <<8));
	else if (drive)
		SoundEffect(195, &boat->pos, PITCH_SHIFT + (0x10000 - (BOAT_MAX_SPEED - binfo->pitch)*100 <<8));

	/* If boat is moving, then do wake */
	if (boat->speed && water-5 == boat->pos.y_pos)
		DoWakeEffect(boat);

	if (lara.skidoo != item_number)
		return;

	/* Wait for last frame of getoff anims before returning to normal Lara control */
	if ((lara_item->current_anim_state == BOAT_JUMPR || lara_item->current_anim_state == BOAT_JUMPL) &&
		  lara_item->frame_number == anims[lara_item->anim_number].frame_end)
	{
		sint16 room_number;
		int x, y, z;
		FLOOR_INFO *floor;

		if (lara_item->current_anim_state == BOAT_JUMPL)
			lara_item->pos.y_rot -= 0x4000;
		else
			lara_item->pos.y_rot += 0x4000;

		lara_item->anim_number = 77;
		lara_item->frame_number = anims[lara_item->anim_number].frame_base;
		lara_item->current_anim_state = lara_item->goal_anim_state = AS_FORWARDJUMP;
		lara_item->gravity_status = 1;
		lara_item->fallspeed = -40;
		lara_item->speed = 20;
		lara_item->pos.x_rot = lara_item->pos.z_rot = 0;
		lara.skidoo = NO_ITEM;
//		lara.gun_status = LG_ARMLESS; // SONY BUG FIX A18 - This line broke Lara's flare anims

		room_number = lara_item->room_number;
		x = lara_item->pos.x_pos + (360 * phd_sin(lara_item->pos.y_rot) >> W2V_SHIFT);
		y = lara_item->pos.y_pos - 90;
		z = lara_item->pos.z_pos + (360 * phd_cos(lara_item->pos.y_rot) >> W2V_SHIFT);
		floor = GetFloor(x, y, z, &room_number);
		if (GetHeight(floor, x, y, z) >= y-STEP_L)
		{
			lara_item->pos.x_pos = x;
			lara_item->pos.z_pos = z;
			if (room_number != lara_item->room_number)
				ItemNewRoom(lara.item_number, room_number);
		}
		lara_item->pos.y_pos = y;

		/* Set boat to still anim */
		boat->anim_number = objects[MOTORBOAT].anim_index + 0;
		boat->frame_number = anims[boat->anim_number].frame_base;
	}
}


