/****************************************************************************
*
* SUB.C
*
* PROGRAMMER : Chris
*    VERSION : 00.00
*    CREATED : 20/09/98
*   MODIFIED : 20/09/98
*	   TABS : 5
*
* DESCRIPTION
*	Submarine Thing controller
*
*****************************************************************************/

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

#ifdef PSX_VERSION
#include <sys\types.h>
#include <libgte.h>
#include <libgpu.h>
#include <gtemac.h>
#include <inline_c.h>
#include <stdio.h>
#include "../spec_psx/typedefs.h"
#include "../spec_psx/psxinput.h"
#include "../spec_psx/malloc.h"
#include "../spec_psx/specific.h"
#include "../spec_psx/maths.h"
#include "../spec_psx/gen_draw.h"
#define SUBINFO_ONLY
#else
#include "../specific/standard.h"
#include "../specific/global.h"
#include "../specific/input.h"
#endif

extern sint16 rcossin_tbl[];


#include "objects.h"
#include "lara.h"
#include "laraanim.h"
#include "laraswim.h"
#include "control.h"
#include "input.h"
#include "camera.h"
#include "sound.h"
#include "sphere.h"
#include "traps.h"
#include "laraflar.h"
#include "lot.h"
#include "effect2.h"
#include "text.h"
#include "savegame.h"
#include "sub.h"

static void TriggerSubMist(long x, long y, long z, long speed, sint16 angle);

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

#define DEBUG_SUB

#define SUB_DRAW_SHIFT	128

/* -------- flags */

#define SF_CONTROL	1
#define SF_SURFACE	2
#define SF_DIVE	4
#define SF_DEAD	8

/* -------- dynamics (16bit precision) */

#define ACCELERATION	0x40000
#define FRICTION		0x18000
#define MAX_SPEED		0x400000

#define ROT_ACCELERATION	0x400000
#define ROT_SLOWACCEL	0x200000
#define ROT_FRICTION 	0x100000
#define MAX_ROTATION	0x1c00000

#define UPDOWN_ACCEL	((ONE_DEGREE*2)<<16)
#define UPDOWN_SLOWACCEL	(((ONE_DEGREE*3)/2)<<16)
#define UPDOWN_FRICTION	((ONE_DEGREE/1)<<16)
#define MAX_UPDOWN		((ONE_DEGREE*2)<<16)

#define UPDOWN_LIMIT	(ONE_DEGREE*80)
#define UPDOWN_SPEED	10

#define SURFACE_DIST	210
#define SURFACE_ANGLE	(ONE_DEGREE*50)
#define DIVE_ANGLE		(ONE_DEGREE*15)
#define DIVE_SPEED		(ONE_DEGREE*5)

/* -------- collision */

#define SUB_RADIUS		300
#define SUB_HEIGHT		400
#define SUB_LENGTH		1024
#define FRONT_TOLERANCE	((45*ONE_DEGREE)<<16)
#define TOP_TOLERANCE	((45*ONE_DEGREE)<<16)
#define WALLDEFLECT		((2*ONE_DEGREE)<<16)

#define GETOFF_DIST 	1024		// minimum collision distance

/* -------- weapon defines */

#define HARPOON_SPEED	256
#define HARPOON_TIME	256
#define HARPOON_RELOAD	15	//30*2

/* -------- animation states */

enum {
	SUBS_DIE,
	SUBS_HIT,
	SUBS_GETOFFS,
	SUBS_1,
	SUBS_MOVE,
	SUBS_POSE,
	SUBS_2,
	SUBS_3,
	SUBS_GETON,
	SUBS_GETOFF
};

/* -------- animation bases */

#define GF2(a)	(anims[objects[UPV].anim_index+a].frame_base)

#define SUB_DEATH1_A		0
#define SUB_DEATH2_A		0
#define SUB_POSE_A			5
#define SUB_POSE_F			GF2(SUB_POSE_A)
#define SUB_GETOFFSURF_A		8
#define SUB_GETOFFSURF_F		GF2(SUB_GETOFFSURF_A)
#define SUB_GETOFFSURF1_A	9
#define SUB_GETOFFSURF1_F	GF2(SUB_GETOFFSURF1_A)
#define SUB_GETONSURF_A		10
#define SUB_GETONSURF_F		GF2(SUB_GETONSURF_A)
#define SUB_GETONSURF1_A		11
#define SUB_GETOFF_A		12
#define SUB_GETOFF_F  		GF2(SUB_GETOFF_A)
#define SUB_GETON_A			13
#define SUB_GETON_F			GF2(SUB_GETON_A)

#define UWDEATH_A			124
#define UWDEATH_F			GF(UWDEATH_A, 17)

#define NUM_WAKE_SPRITES	32
#define WAKE_SIZE 		32
#define WAKE_SPEED 		4

/*---------------------------------------------------------------------------
 * Externals
\*--------------------------------------------------------------------------*/

extern char exit_message[];

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

#if defined(PSX_VERSION) && defined(RELOC)

void SubInitialise(sint16 item_number);
int  SubControl();
void SubCollision(sint16 item_number, ITEM_INFO *litem, COLL_INFO *coll);
void SubDraw(ITEM_INFO *item);
void SubEffects(sint16 item_number);

void *func[] = {
	SubInitialise,
	SubControl,
	SubCollision,
	SubDraw,
	SubEffects
};

#endif

/*---------------------------------------------------------------------------
 * Locals
\*--------------------------------------------------------------------------*/

typedef struct {
	sint32 	x[2];
	sint32 	y[2];
	sint32	z[2];
	uchar 	life;
	uchar	pad[3];
} WAKE_PTS;

enum	{	SUB_FAN = 0,
		SUB_FRONT_LIGHT,
		SUB_LEFT_FIN_LEFT,
		SUB_LEFT_FIN_RIGHT,
		SUB_RIGHT_FIN_RIGHT,
		SUB_RIGHT_FIN_LEFT
};

static WAKE_PTS WakePts[NUM_WAKE_SPRITES][2];	// Left and right wake.
static uchar CurrentStartWake = 0;
static uchar WakeShade = 0;

BITE_INFO	sub_bites[6] =	{	{	0,0,0,3		},	// Fan.
						{	0,96,256,0	},	// Front light.
						{	-128,0,-64,1	},	// Left Fin Left.
						{	0,0,-64,1		},	// Left Fin right.
						{	128,0,-64,2	},	// Right Fin Right.
						{	0,0,-64,2		}	// Right fin left.
					};

/*---------------------------------------------------------------------------
 * Macros
\*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
 * Local Functions
\*--------------------------------------------------------------------------*/

/****************************************************************************
* S_DrawWakeFX
*
* SYNOPSIS
*	void S_DrawWakeFX()
*
* PURPOSE
*	Draws any active wake effects
*
* INPUTS
* 	None
*
* RETURNS
*	Nothing
*/
#ifdef PSX_VERSION

static void S_DrawWakeFX(ITEM_INFO *v)
{

	PHD_VECTOR	pos;
	WAKE_PTS 	*w;
	POLY_G4 	*polyg4;
	short	*scrxy;	// X,Y
	long		*scrz;	// Z
	short	*TempMesh;
	uchar	*rgbs;
	long		lp,lp2,current;
	long		x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,rgb12,rgb34;

	mPushMatrix();
//	SetRotMatrix((MATRIX*) Matrix);
//	SetTransMatrix((MATRIX*) Matrix);
//	mTranslateXYZ(lara_item->pos.x_pos,lara_item->pos.y_pos,lara_item->pos.z_pos);
	mTranslateAbsXYZ(lara_item->pos.x_pos,lara_item->pos.y_pos,lara_item->pos.z_pos);

	for (lp2=0;lp2<2;lp2++)
	{
		scrxy = (short *) 0x1f800000;	// Set pointer to scratch pad.
		scrz = (long *) 0x1f800100;	// Set pointer to scratch pad.
		rgbs = (uchar *) 0x1f800200;	// Set pointer to scratch pad.
		TempMesh = (short *) 0x1f800300;	// Set pointer to scratch pad.

		for ( lp=0 ; lp<2; lp++)
		{
			pos.x = sub_bites[2+(lp2<<1)+lp].x;
			pos.y = sub_bites[2+(lp2<<1)+lp].y;
			pos.z = sub_bites[2+(lp2<<1)+lp].z;
			GetJointAbsPosition(v, &pos, sub_bites[2+(lp2<<1)+lp].mesh_num);
			x1 = pos.x;
			y1 = pos.y+SUB_DRAW_SHIFT;
			z1 = pos.z;

			x1 -= lara_item->pos.x_pos;
			y1 -= lara_item->pos.y_pos;
			z1 -= lara_item->pos.z_pos;

			TempMesh[0] = x1;
			TempMesh[1] = y1;
			TempMesh[2] = z1;

			gte_ldv0(TempMesh);
			gte_rtps();
			gte_stsxy(scrxy);
			gte_stsz(scrz);
			scrxy+=2;
			scrz++;
		}

		*rgbs++ = 0;	// Don't want to see the start of the first one.

	//--------------------

		current = (CurrentStartWake-1) & (NUM_WAKE_SPRITES-1);

		for ( lp=0 ; lp<NUM_WAKE_SPRITES ; lp++)
		{
			w = &WakePts[current][lp2];

			if (w->life)
			{
				ulong 	lp3;

				for (lp3=0;lp3<2;lp3++)
				{
					TempMesh[0] = w->x[lp3] - lara_item->pos.x_pos;
					TempMesh[1] = w->y[lp3] - lara_item->pos.y_pos;
					TempMesh[2] = w->z[lp3] - lara_item->pos.z_pos;
					gte_ldv0(TempMesh);
					gte_rtps();
					gte_stsxy(scrxy);
					gte_stsz(scrz);
					scrxy+=2;
					scrz++;
				}

				*rgbs++ = w->life;
			}
			else
			{
				*scrz = -5555;	// Limiter.
    				break;
			}

			current--;
			current &= NUM_WAKE_SPRITES-1;
		}

		if (lp==0)
		{
			mPopMatrix();
			return;	// Nothing to draw.
		}

		scrxy = (short *) 0x1f800000;	// Set pointer to scratch pad.
		scrz = (long *) 0x1f800100;	// Set pointer to scratch pad.
		rgbs = (uchar *) 0x1f800200;	// Set pointer to scratch pad.

		x1 = *scrxy++;
		y1 = *scrxy++;
		x2 = *scrxy++;
		y2 = *scrxy++;
		z1 = *scrz++;
		z2 = *scrz++;
		rgb12 = *rgbs++;
		if (WakeShade < 16)
			rgb12 = (rgb12 * WakeShade) >> 4;

		for (;;)
		{
			z3 = *scrz++;
			if (z3 == -5555)
				break;

			x3 = *scrxy++;
			y3 = *scrxy++;
			x4 = *scrxy++;
			y4 = *scrxy++;
			z4 = *scrz++;
			rgb34 = *rgbs++;

			if (WakeShade < 16)
				rgb34 = (rgb34 * WakeShade) >> 4;

			z1 = (z1+z2+z3+z4)>>2;

			if ((z1 > 256) && (z1 < 0x5000) &&
				x1 > -128 && x2 > -128 && x3 > -128 && x4 > -128 &&
				x1 < 512+128 && x2 < 512+128 && x3 < 512+128 && x4 < 512+128 &&
				y1 > -128 && y2 > -128 && y3 > -128 && y4 > -128 &&
				y1 < 224+128 && y2 < 224+128 && y3 < 224+128 && y4 < 224+128)
			{
				if (db.polyptr >= db.polybuf_limit)
				{
					mPopMatrix();
					return;
				}

				polyg4 = (POLY_G4 *)db.polyptr;
				setlen(polyg4, 8);
				setcode(polyg4, 0x3a);
				setRGB1(polyg4,rgb12>>1,rgb12>>1,rgb12>>1);
				setRGB0(polyg4,rgb12<<1,rgb12<<1,rgb12<<1);
				setRGB3(polyg4,rgb34>>1,rgb34>>1,rgb34>>1);
				setRGB2(polyg4,rgb34<<1,rgb34<<1,rgb34<<1);
				setXY4(polyg4,x1,y1,x2,y2,x3,y3,x4,y4);

				MyaddPrim(db.ot, z1>>3, polyg4);
    				db.polyptr += sizeof(POLY_G4);

				if (db.polyptr >= db.polybuf_limit)
				{
					mPopMatrix();
					return;
				}

				setDrawTPage((DR_TPAGE *)db.polyptr, 0, 1, getTPage(0, 1, 0, 0));
				MyaddPrim( db.ot,z1>>3,(DR_TPAGE *)db.polyptr);
				db.polyptr += sizeof(DR_TPAGE);
			}

			x1 = x3;
			y1 = y3;
			z1 = z3;
			x2 = x4;
			y2 = y4;
			z2 = z4;
			rgb12 = rgb34;
		}
	}

	mPopMatrix();
}

#endif

/****************************************************************************
* DoWake
*
* SYNOPSIS
*	void DoWake(ITEM_INFO *v, sint16 xoff, sint16 zoff)
*
* PURPOSE
*	Initialises a wake effect
*
* INPUTS
*	v 			= ptr to vehicle's item
*	xoff			= ripple x offset
*	zoff			= ripple z offset
*
* RETURNS
*	Nothing
*/

static void DoWake(ITEM_INFO *v, sint16 leftright)
{
	PHD_VECTOR	pos;
	sint32	lp;

	if (!TriggerActive(v))
		return;

	if (WakePts[CurrentStartWake][leftright].life)
		return;
	WakePts[CurrentStartWake][leftright].life = 0x20;
	for (lp=0;lp<2;lp++)
	{
		pos.x = sub_bites[2+(leftright<<1)+lp].x;
		pos.y = sub_bites[2+(leftright<<1)+lp].y;
		pos.z = sub_bites[2+(leftright<<1)+lp].z;
		GetJointAbsPosition(v, &pos, sub_bites[2+(leftright<<1)+lp].mesh_num);
		WakePts[CurrentStartWake][leftright].x[lp] = pos.x;
		WakePts[CurrentStartWake][leftright].y[lp] = pos.y+SUB_DRAW_SHIFT;
		WakePts[CurrentStartWake][leftright].z[lp] = pos.z;
	}

	if (leftright == 1)
	{
		CurrentStartWake++;
		CurrentStartWake &= (NUM_WAKE_SPRITES-1);
	}
}

/****************************************************************************
* UpdateWakeFX
*
* SYNOPSIS
*	void UpdateWakeFX()
*
* PURPOSE
*	Updates any active wake effects
*
* INPUTS
* 	None
*
* RETURNS
*	Nothing
*/
static void UpdateWakeFX()
{
	int lp,lp2;

	for ( lp=0 ; lp<2 ; lp++ )
	{
		for ( lp2=0 ; lp2<NUM_WAKE_SPRITES ; lp2++ )
		{
			if (WakePts[lp2][lp].life)
				WakePts[lp2][lp].life--;
		}
	}
}

/****************************************************************************
* FireSubHarpoon
*
* SYNOPSIS
*	void FireSubHarpoon(ITEM_INFO *v)
*
* PURPOSE
*	Fires a harpoon
*
* INPUTS
*	v		= ptr to vehicle's item
*
* RETURNS
*	Nothing
*/
static void FireSubHarpoon(ITEM_INFO *v)
{
	sint16 item_number;

#ifndef GAMEDEBUG
	if (lara.harpoon.ammo <= 0)
		return;
#endif

	if ((item_number = CreateItem()) != NO_ITEM)
	{
		PHD_VECTOR pos;
		ITEM_INFO *item;
		static char lr=0;

		item = &items[item_number];

		item->object_number = HARPOON_BOLT;
		item->shade = 0x4210|0x8000;
		item->room_number = v->room_number;

		pos.x = (lr) ? 22 : -22;
		pos.y = 24;
		pos.z = 230;
		GetJointAbsPosition(v, &pos, 3);

		item->pos.x_pos = pos.x;
		item->pos.y_pos = pos.y;
		item->pos.z_pos = pos.z;
		InitialiseItem(item_number);

		item->pos.x_rot = v->pos.x_rot;
		item->pos.y_rot = v->pos.y_rot;
		item->pos.z_rot = 0;

		item->fallspeed = (sint16)(-HARPOON_SPEED * phd_sin(item->pos.x_rot) >> W2V_SHIFT);
		item->speed = (sint16)(HARPOON_SPEED * phd_cos(item->pos.x_rot) >> W2V_SHIFT);
		item->hit_points = HARPOON_TIME;
		item->item_flags[0] = 1;

		AddActiveItem(item_number);

		SoundEffect(23, &lara_item->pos, SFX_ALWAYS);

		if (!savegame.bonus_flag)
			lara.harpoon.ammo--;

		savegame.ammo_used++;

		lr ^= 1;
	}
}
/****************************************************************************
* CanGetOff
*
* SYNOPSIS
*	int CanGetOff(ITEM_INFO *v)
*
* PURPOSE
*	Determines wether Lara can get off the vehicle
*
* INPUTS
*	v		= ptr to vehicle's item
*
* RETURNS
*	If Lara can get off the vehicle
*/
static int CanGetOff(ITEM_INFO *v)
{
	FLOOR_INFO *floor;
	sint16 room_number;
	int x, y, z, height, ceiling, speed;
	sint16	yangle;

	if (lara.current_xvel || lara.current_zvel)
		return 0;

	yangle = v->pos.y_rot + 0x8000;
	speed = (GETOFF_DIST * phd_cos(v->pos.x_rot)) >> W2V_SHIFT;
	x = v->pos.x_pos + (speed * phd_sin(yangle) >> W2V_SHIFT);
	z = v->pos.z_pos + (speed * phd_cos(yangle) >> W2V_SHIFT);
	y = v->pos.y_pos - ((GETOFF_DIST * phd_sin(-v->pos.x_rot)) >> W2V_SHIFT);

//##	TriggerFlareSparks(x,y,z,0,0,0,0,0);

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

	height = GetHeight(floor, x, y, z);

//	if ((height_type == BIG_SLOPE)
//	||  (height_type == DIAGONAL)
	if (height == NO_HEIGHT || y > height)
		return 0;

//##	printf("%d\n", height);
//##	if (ABS(height - v->pos.y_pos) > WALL_L/2)
//##		return 0;

	ceiling = GetCeiling(floor, x, y, z);

	if (height - ceiling < 256 || y < ceiling || ceiling == NO_HEIGHT)
		return 0;
//	if ((ceiling - v->pos.y_pos > -256)
//	||  (height - ceiling < LARA_HITE))
//		return 0;

	return 1;
}

static int GetOnSub(sint16 item_number, COLL_INFO *coll)
{
	/* Returns 0 if no get on, 1 if right get on and 2 if left get on */
	int dist;
	sint32 x, y, z;
	ITEM_INFO *v, *l;
	FLOOR_INFO *floor;
	sint16 room_number,ang;
	uint16 tempang;

	l = lara_item;
	v = &items[item_number];

	if ((!(input & IN_ACTION))
	||  (lara.gun_status != LG_ARMLESS)
	||  (l->gravity_status))
		return 0;

/* -------- is Lara close enough to use the vehicle */


	y = abs(l->pos.y_pos - (v->pos.y_pos - 128));
//	printf("ydist %d \n",y);
	if (y > 256)
		return 0;

	x = l->pos.x_pos - v->pos.x_pos;
	z = l->pos.z_pos - v->pos.z_pos;

	dist = (x * x) + (z * z);

//	printf("dist %d \n",mSqrt(dist));
	if (dist > SQUARE(512))
		return 0;

	room_number = v->room_number;
	floor = GetFloor(v->pos.x_pos, v->pos.y_pos, v->pos.z_pos, &room_number);

	if (GetHeight(floor, v->pos.x_pos, v->pos.y_pos, v->pos.z_pos) < -32000)
		return 0;

/* -------- determine Lara's position in relation to the vehicle */

//	ang = phd_atan(v->pos.z_pos-l->pos.z_pos, v->pos.x_pos-l->pos.x_pos);
//	tempang = ang - v->pos.y_rot;

//	if ((tempang > (ONE_DEGREE*255)) && (tempang < (ONE_DEGREE*315)))
//		return 0;
//	else if ((ang > (ONE_DEGREE*45)) && (ang < (ONE_DEGREE*135)))
//		return 0;
	return 1;
}
/****************************************************************************
* DoCurrent
*
* SYNOPSIS
*	void DoCurrent(ITEM_INFO *v)
*
* PURPOSE
*	Pulls the Kayak towards sinks
*
* INPUTS
*	v	= ptr to Kayak's item structure
*
* RETURNS
*	Nothing
*/
static void DoCurrent(ITEM_INFO *item)
{
	PHD_VECTOR target;

	if (!lara.current_active)
	{
		long	shifter,absvel;

		absvel = abs(lara.current_xvel);

		if (absvel > 16)
			shifter = 4;
		else if (absvel > 8)
			shifter = 3;
		else
			shifter = 2;

		lara.current_xvel -= lara.current_xvel >> shifter;

		if (abs(lara.current_xvel) < 4)
			lara.current_xvel = 0;

		absvel = abs(lara.current_zvel);
		if (absvel > 16)
			shifter = 4;
		else if (absvel > 8)
			shifter = 3;
		else
			shifter = 2;

		lara.current_zvel -= lara.current_zvel >> shifter;
		if (abs(lara.current_zvel) < 4)
			lara.current_zvel = 0;

		if (	lara.current_xvel == 0 &&
			lara.current_zvel == 0)
			return;
	}
	else
	{
		long	angle,dx,dz,speed,sinkval;

		sinkval = lara.current_active-1;
		target.x = camera.fixed[sinkval].x;
		target.y = camera.fixed[sinkval].y;
		target.z = camera.fixed[sinkval].z;
		angle = ((mGetAngle(target.x,target.z,lara_item->pos.x_pos,lara_item->pos.z_pos)-0x4000)>>4)&4095;

		dx = target.x - lara_item->pos.x_pos;
		dz = target.z - lara_item->pos.z_pos;

		speed = camera.fixed[sinkval].data;
		dx = (((rcossin_tbl[(angle<<1)] * speed))) >> 2; // / distance)<<4;
		dz = (((rcossin_tbl[(angle<<1)+1] * speed))) >> 2;// / distance)<<4;

		lara.current_xvel += (dx - lara.current_xvel)>>4;
		lara.current_zvel += (dz - lara.current_zvel)>>4;
	}

	/* Move Lara in direction of sink. */

	item->pos.x_pos += lara.current_xvel>>8;
	item->pos.z_pos += lara.current_zvel>>8;

	/* Reset current (will get set again so long as Lara is over triggers) */
	lara.current_active = 0;
}
/****************************************************************************
* BackgroundCollision
*
* SYNOPSIS
*	void BackgroundCollision(ITEM_INFO *v)
*
* PURPOSE
*	Tests collision against the background
*
* INPUTS
*	v			= ptr to vehicle's item
*	l			= ptr to Lara's item
*	sub			= ptr to vehicle's dynamics
*
* RETURNS
*	Nothing
*/
static void BackgroundCollision(ITEM_INFO *v, ITEM_INFO *l, SUBINFO *sub)
{
	int height;
	COLL_INFO cinfo, *coll=&cinfo;

	coll->bad_pos = NO_BAD_POS;
	coll->bad_neg = -SUB_HEIGHT;
	coll->bad_ceiling = SUB_HEIGHT;
	coll->old.x = v->pos.x_pos;
	coll->old.y = v->pos.y_pos;
	coll->old.z = v->pos.z_pos;
	coll->radius = SUB_RADIUS;
	coll->trigger = NULL;
	coll->slopes_are_walls = 0;
	coll->slopes_are_pits = 0;
	coll->lava_is_pit = 0;
	coll->enable_spaz = 0;
	coll->enable_baddie_push = 1;

	if ((v->pos.x_rot >= -16384)
	&&  (v->pos.x_rot <= 16384))
		coll->facing = lara.move_angle = v->pos.y_rot;
	else
		coll->facing = lara.move_angle = v->pos.y_rot-32768;

	height = phd_sin(v->pos.x_rot) * SUB_LENGTH >> W2V_SHIFT;

	if (height < 0)
		height = -height;

	if (height < 200)
		height = 200;

	coll->bad_neg = -height;

	GetCollisionInfo(coll, v->pos.x_pos, v->pos.y_pos+height/2, v->pos.z_pos, v->room_number, height);
	ShiftItem(v, coll);

	if (coll->coll_type == COLL_FRONT)
	{
		if (sub->RotX > FRONT_TOLERANCE)
			sub->RotX += WALLDEFLECT;

		else if (sub->RotX < -FRONT_TOLERANCE)
			sub->RotX -= WALLDEFLECT;
		else
		{
//			if (ABS(sub->Vel) > 0x40000)
//			{
//				l->goal_anim_state = SUBS_HIT;
//				sub->Vel = (-sub->Vel) >> 1;
//			}
//			else
				sub->Vel = 0;
		}
	}

	else if (coll->coll_type == COLL_TOP)
	{
		if (sub->RotX >= -TOP_TOLERANCE)
			sub->RotX -= WALLDEFLECT;
	}

	else if (coll->coll_type == COLL_TOPFRONT)
		sub->Vel = 0;

	else if (coll->coll_type == COLL_LEFT)
		v->pos.y_rot += 5*ONE_DEGREE;

	else if (coll->coll_type == COLL_RIGHT)
		v->pos.y_rot -= 5*ONE_DEGREE;

	else if (coll->coll_type == COLL_CLAMP)
	{
		v->pos.x_pos = coll->old.x; // 2/10/97: attempt to stop dodgy shift
		v->pos.y_pos = coll->old.y;
		v->pos.z_pos = coll->old.z;
		sub->Vel = 0;
		return;
	}

#if 0
	else if ( coll->coll_type==COLL_TOP )
	{
		if ( item->pos.x_rot>= -45*ONE_DEGREE )
			item->pos.x_rot -= UW_WALLDEFLECT;
	}
	else if ( coll->coll_type==COLL_TOPFRONT )
		item->fallspeed = 0;
	else if ( coll->coll_type==COLL_LEFT )
		item->pos.y_rot += 5*ONE_DEGREE;
	else if ( coll->coll_type==COLL_RIGHT )
		item->pos.y_rot -= 5*ONE_DEGREE;
	else if ( coll->coll_type==COLL_CLAMP )
	{
		item->pos.x_pos = coll->old.x; // 2/10/97: attempt to stop dodgy shift
		item->pos.y_pos = coll->old.y;
		item->pos.z_pos = coll->old.z;
		item->fallspeed = 0;
		return;
	}
#endif

	if (coll->mid_floor < 0)
	{
		v->pos.y_pos += coll->mid_floor;
		sub->RotX += WALLDEFLECT;
	}
}
/****************************************************************************
* UserInput
*
* SYNOPSIS
*	void UserInput(ITEM_INFO *v, ITEM_INFO *l, SUBINFO *sub)
*
* PURPOSE
*	Allows the user to control the vehicle
*
* INPUTS
*	v			= ptr to vehicle's item
*	l			= ptr to Lara's item
* 	sub			= ptr to vehicle's dynamics
*
* RETURNS
*	Nothing
*/
static void UserInput(ITEM_INFO *v, ITEM_INFO *l, SUBINFO *sub)
{
	sint16 anim, frame;

	CanGetOff(v);

	anim = l->anim_number - objects[VEHICLE_ANIM].anim_index;
	frame = l->frame_number - anims[l->anim_number].frame_base;

	switch (l->current_anim_state)
	{
	/* --------------------- */
		case SUBS_MOVE:
	/* --------------------- */
			if (l->hit_points <= 0)
			{
				l->goal_anim_state = SUBS_DIE;
				break;
			}

			if (input & IN_LEFT)
				sub->Rot -= ROT_ACCELERATION;

			else if (input & IN_RIGHT)
				sub->Rot += ROT_ACCELERATION;

			if (sub->Flags & SF_SURFACE)
			{
				if (v->pos.x_rot > SURFACE_ANGLE)
					v->pos.x_rot -= ONE_DEGREE;

				else if (v->pos.x_rot < SURFACE_ANGLE)
					v->pos.x_rot += ONE_DEGREE;
			}
			else
			{
				if (input & IN_FORWARD)
					sub->RotX -= UPDOWN_ACCEL;

				else if (input & IN_BACK)
					sub->RotX += UPDOWN_ACCEL;
			}

			if (input & IN_JUMP)
			{
				if ((sub->Flags & SF_SURFACE)
				&&  (input & IN_FORWARD)
				&&  (v->pos.x_rot > -DIVE_ANGLE))
					sub->Flags |= SF_DIVE;

				sub->Vel += ACCELERATION;
			}

			else
				l->goal_anim_state = SUBS_POSE;

			break;
	/* --------------------- */
		case SUBS_POSE:
	/* --------------------- */
			if (l->hit_points <= 0)
			{
				l->goal_anim_state = SUBS_DIE;
				break;
			}

			if (input & IN_LEFT)
				sub->Rot -= ROT_SLOWACCEL;

			else if (input & IN_RIGHT)
				sub->Rot += ROT_SLOWACCEL;

			if (sub->Flags & SF_SURFACE)
			{
				if (v->pos.x_rot > SURFACE_ANGLE)
					v->pos.x_rot -= ONE_DEGREE;

				else if (v->pos.x_rot < SURFACE_ANGLE)
					v->pos.x_rot += ONE_DEGREE;
			}
			else
			{
				if (input & IN_FORWARD)
					sub->RotX -= UPDOWN_ACCEL;

				else if (input & IN_BACK)
					sub->RotX += UPDOWN_ACCEL;
			}

			if ((input & IN_ROLL) && (CanGetOff(v)))
			{
				if (sub->Flags & SF_SURFACE)
					l->goal_anim_state = SUBS_GETOFFS;
				else
					l->goal_anim_state = SUBS_GETOFF;

				//v->pos.x_rot = 0;

				sub->Flags &= ~SF_CONTROL;

				StopSoundEffect(346);
				SoundEffect(348, (PHD_3DPOS *)&v->pos.x_pos, SFX_ALWAYS);
			}

			else if (input & IN_JUMP)
			{
				if ((sub->Flags & SF_SURFACE)
				&&  (input & IN_FORWARD)
				&&  (v->pos.x_rot > -DIVE_ANGLE))
					sub->Flags |= SF_DIVE;

				l->goal_anim_state = SUBS_MOVE;
			}

			break;
	/* --------------------- */
		case SUBS_GETON:
	/* --------------------- */
			if (anim == SUB_GETONSURF1_A)
			{
				v->pos.y_pos += 4;
				v->pos.x_rot += (ONE_DEGREE);

				if (frame == 30)
					SoundEffect(347, (PHD_3DPOS *)&v->pos.x_pos, SFX_ALWAYS);

				if (frame == 50)
					sub->Flags |= SF_CONTROL;
			}

			else if (anim == SUB_GETON_A)
			{
				if (frame == 30)
					SoundEffect(347, (PHD_3DPOS *)&v->pos.x_pos, SFX_ALWAYS);

				if (frame == 42)
					sub->Flags |= SF_CONTROL;
			}

			break;
	/* --------------------- */
		case SUBS_GETOFF:
	/* --------------------- */
			if ((anim == SUB_GETOFF_A)
			&&  (frame == 42))
			{
				PHD_VECTOR vec = { 0, 0, 0};
				GAME_VECTOR VPos, LPos;

				GetLaraHandAbsPosition(&vec, LARA_HIPS);

				LPos.x = vec.x;
				LPos.y = vec.y;
				LPos.z = vec.z;
				LPos.room_number = v->room_number;
				VPos.x = v->pos.x_pos;
				VPos.y = v->pos.y_pos;
				VPos.z = v->pos.z_pos;
				VPos.room_number = v->room_number;
				mgLOS(&VPos, &LPos, 0);

				l->pos.x_pos = LPos.x;
				l->pos.y_pos = LPos.y;
				l->pos.z_pos = LPos.z;
				l->anim_number = TREAD_A;
				l->frame_number = TREAD_F;
				l->current_anim_state = AS_TREAD;

				l->fallspeed = 0;
				l->gravity_status = 0;
				l->pos.x_rot = l->pos.z_rot = 0;

				UpdateLaraRoom(l, 0);

				lara.water_status = LARA_UNDERWATER;
				lara.gun_status = LG_ARMLESS;
				lara.skidoo = NO_ITEM;

				v->hit_points = 0;
			}

			break;
	/* --------------------- */
		case SUBS_GETOFFS:
	/* --------------------- */
			if ((anim == SUB_GETOFFSURF1_A)
			&&  (frame == 51))
			{
				sint32 wd, wh, hfw;
				PHD_VECTOR vec = { 0, 0, 0};

				wd = GetWaterDepth(l->pos.x_pos, l->pos.y_pos, l->pos.z_pos, l->room_number);
				wh = GetWaterHeight(l->pos.x_pos, l->pos.y_pos, l->pos.z_pos, l->room_number);

				if (wh != NO_HEIGHT)
					hfw = l->pos.y_pos - wh;
				else
					hfw = NO_HEIGHT;

				GetLaraHandAbsPosition(&vec, LARA_HIPS);

				l->pos.x_pos = vec.x;
				l->pos.y_pos = l->pos.y_pos+1 - hfw;
				l->pos.y_pos = vec.y;
				l->pos.z_pos = vec.z;
				l->anim_number = SURFTREAD_A;
				l->frame_number = SURFTREAD_F;
				l->current_anim_state = l->goal_anim_state = AS_SURFTREAD;

				l->fallspeed = 0;
				l->gravity_status = 0;
				l->pos.x_rot = l->pos.z_rot = 0;

				UpdateLaraRoom(l, -LARA_HITE/2);

				lara.water_status = LARA_SURFACE;
				lara.water_surface_dist = -hfw;
				lara.dive_count = DIVE_COUNT+1;
				lara.torso_x_rot = lara.torso_y_rot = 0;
				lara.head_x_rot = lara.head_y_rot = 0;
				lara.gun_status = LG_ARMLESS;
				lara.skidoo = NO_ITEM;

				v->hit_points = 0;
			}

			break;
	/* --------------------- */
		case SUBS_DIE:
	/* --------------------- */
			if (((anim == SUB_DEATH1_A) && (frame == 16))
			||  ((anim == SUB_DEATH2_A) && (frame == 17)))
			{
				PHD_VECTOR vec = { 0, 0, 0};

				GetLaraHandAbsPosition(&vec, LARA_HIPS);

				l->pos.x_pos = vec.x;
				l->pos.y_pos = vec.y;
				l->pos.z_pos = vec.z;
				l->anim_number = UWDEATH_A;
				l->frame_number = UWDEATH_F;
				l->current_anim_state = l->goal_anim_state = AS_UWDEATH;
				l->fallspeed = 0;
				l->gravity_status = 0;
				l->pos.x_rot = l->pos.z_rot = 0;

				sub->Flags |= SF_DEAD;
			}
			v->speed = 0;

			break;
	}

/* -------- auto dive */

	if (sub->Flags & SF_DIVE)
	{
		if (v->pos.x_rot > -DIVE_ANGLE)
			v->pos.x_rot -= DIVE_SPEED;
		else
			sub->Flags &= ~SF_DIVE;
	}

/* -------- slow vehicle with friction */

	if (sub->Vel > 0)
	{
		if ((sub->Vel -= FRICTION) < 0)
			sub->Vel = 0;
	}
	else if (sub->Vel < 0)
	{
		if ((sub->Vel += FRICTION) > 0)
			sub->Vel = 0;
	}

/* -------- limit velocity */

	if (sub->Vel > MAX_SPEED)
		sub->Vel = MAX_SPEED;

	else if (sub->Vel < -MAX_SPEED)
		sub->Vel = -MAX_SPEED;

/* -------- unwind rotations */

	if (sub->Rot > 0)
	{
		if ((sub->Rot -= ROT_FRICTION) < 0)
			sub->Rot = 0;
	}
	else if (sub->Rot < 0)
	{
		if ((sub->Rot += ROT_FRICTION) > 0)
			sub->Rot = 0;
	}

	if (sub->RotX > 0)
	{
		if ((sub->RotX -= UPDOWN_FRICTION) < 0)
			sub->RotX = 0;
	}
	else if (sub->RotX < 0)
	{
		if ((sub->RotX += UPDOWN_FRICTION) > 0)
			sub->RotX = 0;
	}

/* -------- limit rotations */

	if (sub->Rot > MAX_ROTATION)
		sub->Rot = MAX_ROTATION;

	else if (sub->Rot < -MAX_ROTATION)
		sub->Rot = -MAX_ROTATION;

	if (sub->RotX > MAX_UPDOWN)
		sub->RotX = MAX_UPDOWN;

	else if (sub->RotX < -MAX_UPDOWN)
		sub->RotX = -MAX_UPDOWN;
}

/*---------------------------------------------------------------------------
 *	Public Functions
\*--------------------------------------------------------------------------*/

/****************************************************************************
* SubInitialise
*
* SYNOPSIS
*	void SubInitialise(sint16 item_number)
*
* PURPOSE
*	Initialises the submersible
*
* INPUTS
*	item_number	= vehicle item #
*
* RETURNS
*	Nothing
*/
void SubInitialise(sint16 item_number)
{
	ITEM_INFO *v;
	SUBINFO *sub;
	long	i;

	v = &items[item_number];
	sub = (SUBINFO *)v->data = game_malloc(sizeof(SUBINFO), 0);

	sub->Vel = sub->Rot = 0;
	sub->Flags = SF_SURFACE;
	sub->WeaponTimer = 0;

	for ( i=0 ; i<NUM_WAKE_SPRITES ; i++ )
	{
		WakePts[i][0].life = 0;
		WakePts[i][1].life = 0;
	}

}
/****************************************************************************
* SubCollision
*
* SYNOPSIS
*	void SubCollision(sint16 item_number, ITEM_INFO *litem, COLL_INFO *coll)
*
* PURPOSE
*	Determines whether Lara wants to use the submersible
*
* INPUTS
*	item_number	= vehicle item #
*	litem		= ptr to Lara's item structure
*	coll			= ptr to Lara's collision structure
*
* RETURNS
*	Nothing
*/
void SubCollision(sint16 item_number, ITEM_INFO *l, COLL_INFO *coll)
{
	int geton;
	ITEM_INFO *v;

	if ((l->hit_points < 0)
	||  (lara.skidoo != NO_ITEM))
		return;

	v = &items[item_number];

	if ((geton = GetOnSub(item_number, coll)))
	{
		lara.skidoo = item_number;
		lara.water_status = LARA_ABOVEWATER;

	/* -------- throw flare away if using */

		if (lara.gun_type == LG_FLARE)
		{
			CreateFlare(0);
			undraw_flare_meshes();
			lara.flare_control_left = 0;
			lara.request_gun_type = lara.gun_type = LG_ARMLESS;
		}

	/* -------- determine animation */

		lara.gun_status = LG_HANDSBUSY;

		v->hit_points = 1;				// enable collision
		l->pos.x_pos = v->pos.x_pos;
		l->pos.y_pos = v->pos.y_pos;
		l->pos.z_pos = v->pos.z_pos;
		l->pos.y_rot = v->pos.y_rot;

		if ((l->current_anim_state == AS_SURFTREAD)
		||  (l->current_anim_state == AS_SURFSWIM))
		{
			l->anim_number = objects[VEHICLE_ANIM].anim_index + SUB_GETONSURF_A;
			l->frame_number = SUB_GETONSURF_F;
			l->current_anim_state = l->goal_anim_state = SUBS_GETON;
		}
		else
		{
			l->anim_number = objects[VEHICLE_ANIM].anim_index + SUB_GETON_A;
			l->frame_number = SUB_GETON_F;
			l->current_anim_state = l->goal_anim_state = SUBS_GETON;
		}

		AnimateItem(l);
	}
	else
	{
		v->pos.y_pos += SUB_DRAW_SHIFT;
//		coll->enable_baddie_push = 0;
		ObjectCollisionSub(item_number, l, coll);
//		coll->enable_baddie_push = 1;
		v->pos.y_pos -= SUB_DRAW_SHIFT;
	}
}
/****************************************************************************
* SubDraw
*
* SYNOPSIS
*	void SubDraw(ITEM_INFO *v)
*
* PURPOSE
*	Draws the submersible
*
* INPUTS
*	v		= ptr to the submersible item
*
* RETURNS
*	Nothing
*/
void SubDraw(ITEM_INFO *v)
{
	sint16 *frmptr[2];
	OBJECT_INFO *object;
	int clip, rate, frac;

	frac = GetFrames(v, frmptr, &rate);

	object = &objects[v->object_number];

	S_PrintShadow(256, frmptr[0], v, 0);

	phd_PushMatrix();
	phd_TranslateAbs(v->pos.x_pos, v->pos.y_pos+SUB_DRAW_SHIFT, v->pos.z_pos);
	phd_RotYXZ(v->pos.y_rot, v->pos.x_rot, v->pos.z_rot);

	if ((clip = S_GetObjectBounds(frmptr[0])))
	{
		sint32 *bone;
		sint16 **meshpp;
		sint16 *rotation;
		SUBINFO *sub = (SUBINFO *)v->data;

		CalculateObjectLighting(v, frmptr[0]);

		meshpp = &meshes[object->mesh_index];	// Mesh Pointers
		bone = bones + object->bone_index;      // Bone pointer

		if (!frac)
		{
		/* -------- draw body */

			phd_TranslateRel((sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8));
			rotation = frmptr[0] + 9;
			gar_RotYXZsuperpack(&rotation, 0);
			phd_PutPolygons(*meshpp++, clip);

		/* -------- draw left aileron */

			phd_PushMatrix();
			phd_TranslateRel(*(bone+1), *(bone+2), *(bone+3));
			gar_RotYXZsuperpack(&rotation, 0);
			phd_RotX(v->pos.z_rot + (sub->RotX >> 13));
			phd_PutPolygons(*meshpp++, clip);
			phd_PopMatrix();

		/* -------- draw right aileron */

			phd_PushMatrix();
			phd_TranslateRel(*(bone+1+4), *(bone+2+4), *(bone+3+4));
			gar_RotYXZsuperpack(&rotation, 0);
			phd_RotX(-v->pos.z_rot + (sub->RotX >> 13));
			phd_PutPolygons(*meshpp++, clip);
			phd_PopMatrix();

		/* -------- draw fan */

			phd_PushMatrix();
			phd_TranslateRel(*(bone+1+8), *(bone+2+8), *(bone+3+8));
			gar_RotYXZsuperpack(&rotation, 0);
			phd_RotZ(sub->FanRot);
			phd_PutPolygons(*meshpp++, clip);
			phd_PopMatrix();
		}
		else
		{
			sint16 *rotation2;

#ifdef PSX_VERSION
			InitInterpolate(frac, rate, &iMatrixStack[0]);
#else
			InitInterpolate(frac, rate);
#endif

		/* -------- draw body */

			rotation = frmptr[0] + 9;
			rotation2 = frmptr[1]+9;

			phd_TranslateRel_ID((sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8),
							(sint32)*(frmptr[1]+6), (sint32)*(frmptr[1]+7), (sint32)*(frmptr[1]+8));
			gar_RotYXZsuperpack_I(&rotation, &rotation2, 0);
			phd_PutPolygons_I(*meshpp++, clip);

		/* -------- draw left aileron */

			phd_PushMatrix_I();
			phd_TranslateRel_I(*(bone+1), *(bone+2), *(bone+3));
			gar_RotYXZsuperpack_I(&rotation, &rotation2, 0);
			phd_RotX_I(v->pos.z_rot + (sub->RotX >> 13));
			InterpolateMatrix();
			phd_PutPolygons_I(*meshpp++, clip);
			phd_PopMatrix_I();

		/* -------- draw right aileron */

			phd_PushMatrix_I();
			phd_TranslateRel_I(*(bone+1+4), *(bone+2+4), *(bone+3+4));
			gar_RotYXZsuperpack_I(&rotation, &rotation2, 0);
			phd_RotX_I(-v->pos.z_rot + (sub->RotX >> 13));
			phd_PutPolygons_I(*meshpp++, clip);
			phd_PopMatrix_I();

		/* -------- draw fan */

			phd_PushMatrix_I();
			phd_TranslateRel_I(*(bone+1+8), *(bone+2+8), *(bone+3+8));
			gar_RotYXZsuperpack_I(&rotation, &rotation2, 0);
			phd_RotZ_I(sub->FanRot);
			phd_PutPolygons_I(*meshpp++, clip);
			phd_PopMatrix_I();
		}
	}
	phd_PopMatrix();

	if (TriggerActive(v))
		S_DrawWakeFX(v);

}
/****************************************************************************
* SubControl
*
* SYNOPSIS
*	int SubControl()
*
* PURPOSE
*	Submersible vehicle controller
*
* INPUTS
*	None
*
* RETURNS
*	0 if not under vehicle's control
*/
int SubControl()
{
	sint32 h;
	SUBINFO *sub;
	ITEM_INFO *v, *l;
	FLOOR_INFO *floor;
	sint16 room_number;

	l = lara_item;
	v = &items[lara.skidoo];
	sub = (SUBINFO *)v->data;

/* -------- update dynamics */

	if (!(sub->Flags & SF_DEAD))
	{
		UserInput(v, l, sub);

		v->speed = sub->Vel >> 16;

		v->pos.x_rot += sub->RotX >> 16;
		v->pos.y_rot += (sub->Rot >> 16);
		v->pos.z_rot = (sub->Rot >> 12);

		if (v->pos.x_rot > UPDOWN_LIMIT)
			v->pos.x_rot = UPDOWN_LIMIT;

		else if (v->pos.x_rot < -UPDOWN_LIMIT)
			v->pos.x_rot = -UPDOWN_LIMIT;

		v->pos.x_pos += (((phd_sin(v->pos.y_rot) * v->speed) >> W2V_SHIFT) * phd_cos(v->pos.x_rot)) >> W2V_SHIFT;
		v->pos.y_pos -= (phd_sin(v->pos.x_rot) * v->speed) >> W2V_SHIFT;
		v->pos.z_pos += (((phd_cos(v->pos.y_rot) * v->speed) >> W2V_SHIFT) * phd_cos(v->pos.x_rot)) >> W2V_SHIFT;
	}

/* -------- determine if vehicle is near the surface */

	room_number = v->room_number;
	floor = GetFloor(v->pos.x_pos, v->pos.y_pos, v->pos.z_pos, &room_number);
	v->floor = GetHeight(floor, v->pos.x_pos, v->pos.y_pos, v->pos.z_pos);

	if ((sub->Flags & SF_CONTROL)
	&&  (!(sub->Flags & SF_DEAD)))
	{
		h = GetWaterHeight(v->pos.x_pos, v->pos.y_pos, v->pos.z_pos, room_number);

		if ((h != NO_HEIGHT) && (!(room[v->room_number].flags & UNDERWATER)))
		{
			if ((h - v->pos.y_pos) >= -SURFACE_DIST)
				v->pos.y_pos = h + SURFACE_DIST;

			if (!(sub->Flags & SF_SURFACE))
			{
  				SoundEffect(36, &lara_item->pos, SFX_ALWAYS);
				sub->Flags &= ~SF_DIVE;
			}

			sub->Flags |= SF_SURFACE;
		}

		else if ((h != NO_HEIGHT) && ((h - v->pos.y_pos) >= -SURFACE_DIST))
		{
			v->pos.y_pos = h + SURFACE_DIST;

			if (!(sub->Flags & SF_SURFACE))
			{
  				SoundEffect(36, &lara_item->pos, SFX_ALWAYS);
				sub->Flags &= ~SF_DIVE;
			}

			sub->Flags |= SF_SURFACE;
		}

		else
			sub->Flags &= ~SF_SURFACE;

	/* -------- update air bar */

		if (!(sub->Flags & SF_SURFACE))
		{
			if (l->hit_points >= 0)
			{
				lara.air--;

				if (lara.air < 0)
				{
					lara.air = -1;
					l->hit_points -= 5;
				}
			}
		}
		else
		{
			if (l->hit_points >= 0)
			{
				lara.air+=10;

				if (lara.air > LARA_AIR)
					lara.air = LARA_AIR;
			}
		}
	}

	TestTriggers(trigger_index, 0);

/* -------- update vehicle & Lara */

	if ((lara.skidoo != NO_ITEM)
	&&  (!(sub->Flags & SF_DEAD)))
	{
		DoCurrent(v);

		if ((input & IN_ACTION)
		&&  (sub->Flags & SF_CONTROL)
		&&  (!sub->WeaponTimer))
		{
			FireSubHarpoon(v);
			sub->WeaponTimer = HARPOON_RELOAD;
		}

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

		l->pos.x_pos = v->pos.x_pos;
		l->pos.y_pos = v->pos.y_pos + SUB_DRAW_SHIFT;
		l->pos.z_pos = v->pos.z_pos;
		l->pos.x_rot = v->pos.x_rot;
		l->pos.y_rot = v->pos.y_rot;
		l->pos.z_rot = v->pos.z_rot;

		AnimateItem(l);
		BackgroundCollision(v, l, sub);

		if (sub->Flags & SF_CONTROL)
			SoundEffect(346, (PHD_3DPOS *)&v->pos.x_pos, SFX_ALWAYS|PITCH_SHIFT|0x1000000|(v->speed<<16));

		v->anim_number = objects[UPV].anim_index + (l->anim_number - objects[VEHICLE_ANIM].anim_index);
		v->frame_number = anims[v->anim_number].frame_base + (l->frame_number - anims[l->anim_number].frame_base);

		if (sub->Flags & SF_SURFACE)
			camera.target_elevation = -60*ONE_DEGREE;
		else
			camera.target_elevation = 0;

		return 1;
	}

/* -------- Lara is dead */

	else if (sub->Flags & SF_DEAD)
	{
		AnimateItem(l);

		if (room_number != v->room_number)
			ItemNewRoom(lara.skidoo, room_number);

		BackgroundCollision(v, l, sub);

		sub->RotX = 0;

		v->anim_number = SUB_POSE_A;
		v->frame_number = SUB_POSE_F;
		v->goal_anim_state = v->current_anim_state = SUBS_POSE;
		v->fallspeed = 0;
		v->speed = 0;
		v->gravity_status = 1;
		AnimateItem(v);

		return 1;
	}

	else
		return 0;
}
/****************************************************************************
* SubEffects
*
* SYNOPSIS
*	void SubEffects(sint16 item_number)
*
* PURPOSE
*	Adds vehicle effects
*
* INPUTS
*	item_number	= vehicle item #
*
* RETURNS
*	Nothing
*/

void SubEffects(sint16 item_number)
{
	ITEM_INFO *v;
	SUBINFO *sub;
	PHD_VECTOR pos;
	long		lp;

	v = &items[item_number];
	sub = (SUBINFO *)v->data;

/* -------- Lara is using this vehicle */

	if (lara.skidoo == item_number)
	{
		if (!sub->Vel)
			sub->FanRot += (ONE_DEGREE*2);
		else
			sub->FanRot += sub->Vel >> 12;

		if (sub->Vel)
		{
			pos.x = sub_bites[SUB_FAN].x;
			pos.y = sub_bites[SUB_FAN].y;
			pos.z = sub_bites[SUB_FAN].z;
			GetJointAbsPosition(v, &pos, sub_bites[SUB_FAN].mesh_num);
			TriggerSubMist(pos.x,pos.y+SUB_DRAW_SHIFT,pos.z,abs(sub->Vel)>>16,v->pos.y_rot+0x8000);

			if ((GetRandomControl()&1)==0)
			{
				PHD_3DPOS pos3d;
				sint16 	room_number;

				pos3d.x_pos = pos.x+(GetRandomControl()&63)-32;
				pos3d.y_pos = pos.y+SUB_DRAW_SHIFT;
				pos3d.z_pos = pos.z+(GetRandomControl()&63)-32;
				room_number = v->room_number;
				GetFloor(pos3d.x_pos,pos3d.y_pos,pos3d.z_pos,&room_number);
				CreateBubble((PHD_3DPOS *)&pos3d, room_number, 4, 8);
			}
		}
	}

	for (lp=0;lp<2;lp++)
	{
		GAME_VECTOR	source,target;
		long		r;

		r = 31-(GetRandomControl()&3);
		pos.x = sub_bites[SUB_FRONT_LIGHT].x;
		pos.y = sub_bites[SUB_FRONT_LIGHT].y;
		pos.z = sub_bites[SUB_FRONT_LIGHT].z<<(lp*6);
		GetJointAbsPosition(v, &pos, sub_bites[SUB_FRONT_LIGHT].mesh_num);

		if (lp==1)	// LOS light?
		{
			target.x = pos.x;
			target.y = pos.y;
			target.z = pos.z;
			target.room_number = v->room_number;
			LOS(&source, &target);
			pos.x = target.x;
			pos.y = target.y;
			pos.z = target.z;
		}
		else
		{
			source.x = pos.x;
			source.y = pos.y;
			source.z = pos.z;
			source.room_number = v->room_number;
		}

		TriggerDynamic(pos.x, pos.y, pos.z, 16+(lp<<3), r,r,r);
	}

	if (!(wibble & 15) && sub->Vel)
	{
		DoWake(v, 0);	// Left.
 		DoWake(v, 1);	// Right.
	}

	if (sub->Vel == 0)
	{
		if (WakeShade)
			WakeShade--;
	}
	else
	{
		if (WakeShade < 16)
			WakeShade++;
	}

	if (sub->WeaponTimer)
		sub->WeaponTimer--;

	UpdateWakeFX();
}

static void TriggerSubMist(long x, long y, long z, long speed, sint16 angle)
{
	long		size,xv,zv;
	SPARKS	*sptr;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 0;
	sptr->sG = 0;
	sptr->sB = 0;

	sptr->dR = 64;
	sptr->dG = 64;
	sptr->dB = 64;

	sptr->ColFadeSpeed = 4 + (GetRandomControl()&3);
	sptr->FadeToBlack = 12;
	sptr->sLife = sptr->Life = (GetRandomControl()&3) + 20;

	sptr->TransType = COLADD;

	sptr->extras = 0;
	sptr->Dynamic = -1;

	sptr->x = x + ((GetRandomControl()&15)-8);
	sptr->y = y + ((GetRandomControl()&15)-8);
	sptr->z = z + ((GetRandomControl()&15)-8);
	zv = (speed * phd_cos(angle)) >> (W2V_SHIFT+2);
	xv = (speed * phd_sin(angle)) >> (W2V_SHIFT+2);
	sptr->Xvel = xv + ((GetRandomControl()&127)-64);
	sptr->Yvel = 0;
	sptr->Zvel = zv + ((GetRandomControl()&127)-64);
	sptr->Friction = 3;

	if (GetRandomControl()&1)
	{
		sptr->Flags = SP_SCALE|SP_DEF|SP_ROTATE|SP_EXPDEF;
		sptr->RotAng = GetRandomControl()&4095;
		if (GetRandomControl()&1)
			sptr->RotAdd = -(GetRandomControl()&15)-16;
		else
			sptr->RotAdd = (GetRandomControl()&15)+16;
	}
	else
		sptr->Flags = SP_SCALE|SP_DEF|SP_EXPDEF;

	sptr->Def = objects[EXPLOSION1].mesh_index;
	sptr->Scalar = 3;
	sptr->Gravity = sptr->MaxYvel = 0;
	size = (GetRandomControl()&7) + (speed>>1) + 16;
	sptr->Width = sptr->sWidth = size>>2;
 	sptr->Height = sptr->sHeight = size>>2;
	sptr->dWidth = size;
	sptr->dHeight = size;
}


