/*********************************************************************************************/
/*                                                                                           */
/* Traps                                                                                     */
/*                                                                                           */
/*********************************************************************************************/

#include "game.h"

/******************************** TYPE DEFINITIONS *******************************************/

extern short rcossin_tbl[];

#define BLADE_DAMAGE			100
#define STATUE_DAMAGE		20
#define SPIKE_DAMAGE			15
#define TEETHTRAP_DAMAGE 		400
#define FALLING_CEILING_DAMAGE 	300
#define ROLLINGBALL_DAMAGE_AIR	100
#define ICICLE_DAMAGE				200
#define SPINBLADE_DAMAGE			100
#define HOOK_DAMAGE				50
#define PROP_DAMAGE				200
#define SPIKEWALL_DAMAGE		20
#define DART_SPEED			256

#define FLAME_TOONEAR_DAMAGE 5
#define FLAME_ONFIRE_DAMAGE  7

#define LAVA_GLOB_DAMAGE 10

#define WEDGE_SPEED 25
#define STATUE_TOUCH 0x80

enum	{ BIG_FIRE = 0, SMALL_FIRE, JET_FIRE, SIDE_FIRE };

enum statue_anims {STATUE_EMPTY, STATUE_STOP, STATUE_CUT};
enum spin_anims {SPIN_EMPTY, SPIN_STOP, SPIN_ROLL};

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

enum prop_anims {PROP_ON, PROP_OFF};

void PropellerControl(sint16 item_number)
{
	// also FAN and SAW control
	ITEM_INFO *item;

	item = &items[item_number];

	if (TriggerActive(item) && !(item->flags & ONESHOT))
	{
		item->goal_anim_state = PROP_ON;

		if (item->touch_bits & 6)
		{
			lara_item->hit_points -= PROP_DAMAGE;
			lara_item->hit_status = 1;

			DoLotsOfBlood(lara_item->pos.x_pos, lara_item->pos.y_pos-(STEP_L*2), lara_item->pos.z_pos,
							(sint16)(GetRandomDraw()>>10), (sint16)(item->pos.y_rot+0x4000), lara_item->room_number, 3);

			if (item->object_number == SAW)
				SoundEffect(207, &item->pos, 0);
		}
		else if (item->object_number == SAW)
			SoundEffect(206, &item->pos, 0);
		else if (item->object_number == FAN)
			SoundEffect(215, &item->pos, UNDERWATER);
		else
			SoundEffect(217, &item->pos, 0);
	}
	else if (item->goal_anim_state != PROP_OFF)
	{
		if (item->object_number == FAN)
			SoundEffect(216, &item->pos, UNDERWATER);
		item->goal_anim_state = PROP_OFF;
	}

	AnimateItem(item);

	if (item->status == DEACTIVATED)
	{
		RemoveActiveItem(item_number);
		if (item->object_number != SAW)
			item->collidable = 0;
	}
}

int detonate_all_mines = 0;

void MineControl(sint16 mine_number)
{
	FX_INFO *fx;
	ITEM_INFO *item, *mine;
	FLOOR_INFO *floor;
	sint16 item_number, room_number, fx_number;

	mine = &items[mine_number];
	if (mine->flags & ONESHOT)
		return;

	if (!detonate_all_mines)
	{
		/* ASSUMPTION: mine is underwater and there is a room above that is the one which may or may not have a boat in */
		room_number = mine->room_number;
		GetFloor(mine->pos.x_pos, mine->pos.y_pos-WALL_L*2, mine->pos.z_pos, &room_number);

		/* Check for boat collision */
		item_number = room[room_number].item_number;
		while (item_number != NO_ITEM)
		{
			item = &items[item_number];
			if (item->object_number == MOTORBOAT && SQUARE(item->pos.x_pos - mine->pos.x_pos) + SQUARE(item->pos.z_pos - mine->pos.z_pos) < SQUARE(WALL_L/2))
				break;
			item_number = item->next_item;
		}

		if (item_number == NO_ITEM)
			return;

		/* KABOOM! Goodbye boat */
		if (lara.skidoo == item_number)
		{
			ExplodingDeath(lara.item_number, 0xffffffff, 0);
			lara_item->hit_points = 0;
			lara_item->flags |= ONESHOT; // flag Lara as invisible
		}

		item->object_number = BOAT_BITS; // change to bits
		ExplodingDeath(item_number, 0xffffffff, 0);
		KillItem(item_number);
		item->object_number = MOTORBOAT; // change back for savegame to work OK

		// Test HEAVY triggers
		floor = GetFloor(mine->pos.x_pos, mine->pos.y_pos, mine->pos.z_pos, &room_number);
		GetHeight(floor, mine->pos.x_pos, mine->pos.y_pos, mine->pos.z_pos);
		TestTriggers(trigger_index, 1);

		detonate_all_mines = 1; // and the rest go up too
	}
	else if (GetRandomControl() < 0x7800)
		return;

	fx_number = CreateEffect(mine->room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->pos.x_pos = mine->pos.x_pos;
		fx->pos.y_pos = mine->pos.y_pos-WALL_L;
		fx->pos.z_pos = mine->pos.z_pos;
		fx->speed = 0;
		fx->frame_number = 0;
		fx->counter = 0;
		fx->object_number = EXPLOSION1;
	}

	Splash(mine);

	SoundEffect(105, &mine->pos, 0);
	mine->flags |= ONESHOT;
	mine->mesh_bits = 1;
	mine->collidable = 0;
}


#define SPIKE_SPEED 1

void ControlSpikeWall(sint16 item_number)
{
	ITEM_INFO *item;
	FLOOR_INFO *floor;
	sint32 x, z;
	sint16 room_number;

	item = &items[item_number];

	/* Move wall */
	if (TriggerActive(item) && item->status != DEACTIVATED)
	{
		z = item->pos.z_pos + (SPIKE_SPEED * phd_cos(item->pos.y_rot) >> WALL_SHIFT);
		x = item->pos.x_pos + (SPIKE_SPEED * phd_sin(item->pos.y_rot) >> WALL_SHIFT);

		room_number = item->room_number;
		floor = GetFloor(x, item->pos.y_pos, z, &room_number);

		if (GetHeight(floor, x, item->pos.y_pos, z) != item->pos.y_pos)
			item->status = DEACTIVATED;
		else
		{
			item->pos.z_pos = z;
			item->pos.x_pos = x;
			if (room_number != item->room_number)
				ItemNewRoom(item_number, room_number);
		}

		SoundEffect(204, &item->pos, 0);
	}

	/* Splot Lara */
	if (item->touch_bits)
	{
		lara_item->hit_points -= SPIKEWALL_DAMAGE;
		lara_item->hit_status = 1;

		DoLotsOfBlood(lara_item->pos.x_pos, lara_item->pos.y_pos-(WALL_L/2), lara_item->pos.z_pos,
							SPIKE_SPEED, item->pos.y_rot, lara_item->room_number, 3);
		item->touch_bits = 0;

		SoundEffect(205, &item->pos, 0);
	}
}


void ControlCeilingSpikes(sint16 item_number)
{
	ITEM_INFO *item;
	FLOOR_INFO *floor;
	sint32 y;
	sint16 room_number;

	item = &items[item_number];

	/* Move wall */
	if (TriggerActive(item) && item->status != DEACTIVATED)
	{
		y = item->pos.y_pos + 5;

		room_number = item->room_number;
		floor = GetFloor(item->pos.x_pos, y, item->pos.z_pos, &room_number);

		if (GetHeight(floor, item->pos.x_pos, y, item->pos.z_pos) < y+WALL_L)
			item->status = DEACTIVATED;
		else
		{
			item->pos.y_pos = y;
			if (room_number != item->room_number)
				ItemNewRoom(item_number, room_number);
		}

		SoundEffect(204, &item->pos, 0);
	}

	/* Splot Lara */
	if (item->touch_bits)
	{
		lara_item->hit_points -= SPIKEWALL_DAMAGE;
		lara_item->hit_status = 1;

		DoLotsOfBlood(lara_item->pos.x_pos, item->pos.y_pos+WALL_L*3/4, lara_item->pos.z_pos,
							SPIKE_SPEED, item->pos.y_rot, lara_item->room_number, 3);
		item->touch_bits = 0;

		SoundEffect(205, &item->pos, 0);
	}
}


void HookControl(sint16 item_number)
{
	static int clonked = 0; // assumes Lara doesn't get clonked by 2 different hooks on successive frames
	ITEM_INFO *item;

	item = &items[item_number];

	if (item->touch_bits && !clonked)
	{
		lara_item->hit_points -= HOOK_DAMAGE;
		lara_item->hit_status = 1;

		DoLotsOfBlood(lara_item->pos.x_pos, lara_item->pos.y_pos-(WALL_L/2), lara_item->pos.z_pos,
							lara_item->speed, lara_item->pos.y_rot, lara_item->room_number, 3);
	}
	else
		clonked = 0;

	AnimateItem(item);
}

void SpinningBlade(sint16 item_number)
{
	ITEM_INFO *item;
	FLOOR_INFO *floor;
	sint32 z, x, height, spinning;
	sint16 room_number;

	item = &items[item_number];

	/* Move until it hits any floor change */
	if (item->current_anim_state == SPIN_ROLL)
	{
		if (item->goal_anim_state != SPIN_STOP)
		{
			z = item->pos.z_pos + (WALL_L*3/2 * phd_cos(item->pos.y_rot) >> W2V_SHIFT);
			x = item->pos.x_pos + (WALL_L*3/2 * phd_sin(item->pos.y_rot) >> W2V_SHIFT);

			room_number = item->room_number;
			floor = GetFloor(x, item->pos.y_pos, z, &room_number);
			height = GetHeight(floor, x, item->pos.y_pos, z);

			//if (ABS(height - item->pos.y_pos) > WALL_L)
			if (height == NO_HEIGHT)
				item->goal_anim_state = SPIN_STOP;
		}
		spinning = 1;

		/* Hurt Lara */
		if (item->touch_bits)
		{
			lara_item->hit_status = 1;
			lara_item->hit_points -= SPINBLADE_DAMAGE;

			// more blood required
			DoLotsOfBlood(lara_item->pos.x_pos, lara_item->pos.y_pos-STEP_L*2, lara_item->pos.z_pos,
				(sint16)(item->speed*2), lara_item->pos.y_rot, lara_item->room_number, 2);
		}

		SoundEffect(231, &item->pos, 0);
	}
	else
	{
		if (TriggerActive(item))
			item->goal_anim_state = SPIN_ROLL;
		spinning = 0;
	}


	AnimateItem(item);

	room_number = item->room_number;
	floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
	item->floor = item->pos.y_pos = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	if (room_number != item->room_number)
		ItemNewRoom(item_number, room_number);

	/* If it has just stopped, turn it through 180 degrees so it can go back again */
	if (spinning && item->current_anim_state == SPIN_STOP)
		item->pos.y_rot += -0x8000;
}

enum icicle_anims {ICICLE_EMPTY, ICICLE_BREAK, ICICLE_FALL, ICICLE_LAND};

void IcicleControl(sint16 item_number)
{
	sint16 room_number;
	ITEM_INFO *item;
	FLOOR_INFO *floor;

	item = &items[item_number];

	switch (item->current_anim_state)
	{
	case ICICLE_BREAK:
		/* Make it fall the moment it is triggered? */
		item->goal_anim_state = ICICLE_FALL;
		break;

	case ICICLE_FALL:
		if (item->gravity_status == 0)
		{
			item->fallspeed = 50;
			item->gravity_status = 1;
		}

		if (item->touch_bits)
		{
			lara_item->hit_points -= ICICLE_DAMAGE;
			lara_item->hit_status = 1;
		}
		break;

	case ICICLE_LAND:
		item->gravity_status = 0;
		break;
	}

	AnimateItem(item);
	if ( item->status==DEACTIVATED )
	{
		RemoveActiveItem(item_number);
		return;
	}

	room_number = item->room_number;
	floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
	if (item->room_number != room_number)
		ItemNewRoom(item_number, room_number);
	item->floor = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);

	/* Check for landing on floor */
	if (item->current_anim_state == ICICLE_FALL && item->pos.y_pos >= item->floor)
	{
		item->goal_anim_state = ICICLE_LAND;
		item->pos.y_pos = item->floor;
		item->fallspeed = 0;
		item->gravity_status = 0;
		item->mesh_bits = 0x2b;
	}
}

void InitialiseBlade(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	item->anim_number = objects[BLADE].anim_index + 2;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = STATUE_STOP;
}

void BladeControl(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	if (TriggerActive(item) && item->current_anim_state == STATUE_STOP)
		item->goal_anim_state = STATUE_CUT;
	else
		item->goal_anim_state = STATUE_STOP;

	if ((item->touch_bits & 0x2) && item->current_anim_state == STATUE_CUT)
	{
		lara_item->hit_status = 1;
		lara_item->hit_points -= BLADE_DAMAGE;

		// more blood required
		DoLotsOfBlood(lara_item->pos.x_pos, item->pos.y_pos-STEP_L, lara_item->pos.z_pos,
			lara_item->speed, lara_item->pos.y_rot, lara_item->room_number, 2);
	}

	AnimateItem(item);
}


void InitialiseKillerStatue(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	item->anim_number = objects[item->object_number].anim_index + 3;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = STATUE_STOP;
}

void KillerStatueControl(sint16 item_number)
{
	ITEM_INFO *item;
	sint32 x, y, z;
	sint16 d;

	item = &items[item_number];

	if (TriggerActive(item) && item->current_anim_state == STATUE_STOP)
		item->goal_anim_state = STATUE_CUT;
	else
		item->goal_anim_state = STATUE_STOP;

	if ((item->touch_bits & STATUE_TOUCH) && item->current_anim_state == STATUE_CUT)
	{
		lara_item->hit_status = 1;
		lara_item->hit_points -= STATUE_DAMAGE;

		// this bit copied from pendulum
		x = lara_item->pos.x_pos + (GetRandomControl()-16384)/256;
		z = lara_item->pos.z_pos + (GetRandomControl()-16384)/256;
		y = lara_item->pos.y_pos - GetRandomControl()/44;   // 750 random range
		d = (GetRandomControl()-16384)/8 + lara_item->pos.y_rot;
		DoBloodSplat(x,y,z, lara_item->speed, d, lara_item->room_number);
	}

	AnimateItem(item);
}


#define BOARD_OFF 0
#define BOARD_ON  1

void SpringBoardControl(sint16 item_number)
{
	/* Fires Lara up into the air */
	ITEM_INFO *item;

	item = &items[item_number];

	if (item->current_anim_state == BOARD_OFF && lara_item->pos.y_pos == item->pos.y_pos &&
		(lara_item->pos.x_pos >> WALL_SHIFT) == (item->pos.x_pos >> WALL_SHIFT) &&
		(lara_item->pos.z_pos >> WALL_SHIFT) == (item->pos.z_pos >> WALL_SHIFT))
	{
		/* If Lara is falling too fast, don't bounce her */
		if (lara_item->hit_points <= 0)
			return;

		if (lara_item->current_anim_state == AS_BACK || lara_item->current_anim_state == AS_FASTBACK)
			lara_item->speed = -lara_item->speed;

		lara_item->fallspeed = -240;
		lara_item->gravity_status = 1;

		/* Need to set Lara's anim frame too */
		lara_item->anim_number = FALLDOWN_A;
		lara_item->frame_number = FALLDOWN_F;
		lara_item->current_anim_state = AS_FORWARDJUMP;
		lara_item->goal_anim_state = AS_FORWARDJUMP;

		/* Make springboard spring */
		item->goal_anim_state = BOARD_ON;
	}

	AnimateItem(item);
}

/******************************************************************************
 *				Initialise Rolling Ball
 *****************************************************************************/
void	InitialiseRollingBall(sint16 item_number)
{
	ITEM_INFO  *item;
	GAME_VECTOR *old;

	item = &items[item_number];
	// Store old Position so Rolling Ball can be reactivated
	old = item->data = (GAME_VECTOR *)game_malloc( sizeof(GAME_VECTOR),ROLLINGBALL_STUFF );
	old->x = item->pos.x_pos;
	old->y = item->pos.y_pos;
	old->z = item->pos.z_pos;
	old->room_number = item->room_number;
}

/******************************************************************************
 *			Rolling Ball Control routine
 *****************************************************************************/
#define ROLL_SHAKE_RANGE (WALL_L*10)

void	RollingBallControl(sint16 item_number)
{
	sint32 y1, y2;
	sint32 		x,z,dist;
	sint32		oldx,oldz;
	ITEM_INFO 	*item;
	FLOOR_INFO 	*floor;
	GAME_VECTOR	*old;
	ROOM_INFO	*r;
	sint16 		room_number;

	item = &items[item_number];
	if ( item->status==ACTIVE )
	{
		// 'TRAP_WORKING' means AVALANCHE or OILDRUMS have hit wall and are stopping
		if (item->goal_anim_state == TRAP_WORKING)
		{
			AnimateItem(item);
			return;
		}

		// Make ball drop under influence of gravity until it hits the ground
		if ( item->pos.y_pos<item->floor )                  // Make Ball drop under influence
		{                                                   // of gravity
			if ( !item->gravity_status )
			{
				item->gravity_status = 1;
				item->fallspeed = -10;
			}
		}
		else if ( item->current_anim_state==TRAP_SET )		// Only activate Rolling Ball
			item->goal_anim_state = TRAP_ACTIVATE;          // When it Hits ground if starts in Air

		oldx = item->pos.x_pos;								// Save Old Position
		oldz = item->pos.z_pos;                             // in case of badness
		AnimateItem(item);

		room_number = item->room_number;
		floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
		if (item->room_number != room_number)
			ItemNewRoom(item_number, room_number);

		item->floor = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);

		// Test for HEAVY triggers
  		TestTriggers(trigger_index, 1);

		if ( item->pos.y_pos>=item->floor-STEP_L )
		{
			item->gravity_status = 0;					// If MidPoint Below Floor
			item->fallspeed = 0;
			item->pos.y_pos = item->floor;              // Then Ball is on floor
			if (item->object_number == AVALANCHE)
				SoundEffect(222, &item->pos, 0);
			else if (item->object_number == OILDRUMS)
				SoundEffect(227, &item->pos, 0);
			else
				SoundEffect(147, &item->pos, 0);
			dist = phd_sqrt(SQUARE(camera.mike_pos.x - item->pos.x_pos) + SQUARE(camera.mike_pos.z - item->pos.z_pos));
			if (dist < ROLL_SHAKE_RANGE)
				camera.bounce = -40 * (ROLL_SHAKE_RANGE - dist) / ROLL_SHAKE_RANGE;
		}

		/* Check for collision with wall */
//		dist = (item->object_number==ROLLING_BALL)? STEP_L*3/2 : WALL_L;
		dist = (item->object_number==ROLLING_BALL)? (WALL_L) : WALL_L;
		x = item->pos.x_pos + (dist * phd_sin(item->pos.y_rot) >> W2V_SHIFT);
		z = item->pos.z_pos + (dist * phd_cos(item->pos.y_rot) >> W2V_SHIFT);

		floor = GetFloor(x, item->pos.y_pos, z, &room_number);
		y1 = GetHeight(floor, x, item->pos.y_pos, z);

		room_number = item->room_number;
		floor = GetFloor(x, item->pos.y_pos-(WALL_L<<1), z, &room_number);
		y2 = GetCeiling(floor, x, item->pos.y_pos-(WALL_L+256), z);

//		if (GetHeight(floor, x, item->pos.y_pos, z) < item->pos.y_pos)

//		printf("%d %d %d\n",item->pos.y_pos, y1, y2);
		if ((y1 < item->pos.y_pos) || (y2 > (item->pos.y_pos-(WALL_L+256))))
		{
			/* If this is the avalanche version, then bounce off the wall */
			if (item->object_number == AVALANCHE)
			{
				SoundEffect(223, &item->pos, 0);
				item->goal_anim_state = TRAP_WORKING; // anim state 2; means stop in this case
			}
			else if (item->object_number == OILDRUMS)
			{
				SoundEffect(228, &item->pos, 0);
				item->goal_anim_state = TRAP_WORKING;
			}
			else
				item->status = DEACTIVATED;          		// Ball Has hit wall so make it Deactivated
			item->pos.y_pos = item->floor;              // Use old position so Lara
			item->pos.x_pos = oldx;                     // can get round the Back
			item->pos.z_pos = oldz;
			item->speed = item->fallspeed = 0;
			item->touch_bits = 0;
		}
	}
	// Rolling Ball has come to a rest So do we Need to Reset It
	else if ( item->status==DEACTIVATED )
	{
		// MAY Need A Test for Heavy Triggers Here...
		if ( !TriggerActive(item) )
		{
			item->status = NOT_ACTIVE;
			old = (GAME_VECTOR *)item->data;
			item->pos.x_pos = old->x;
			item->pos.y_pos = old->y;
			item->pos.z_pos = old->z;
			if ( item->room_number!=old->room_number )
			{
				RemoveDrawnItem( item_number );
				r = &room[old->room_number];
				item->next_item = r->item_number;
				r->item_number = item_number;
				item->room_number = old->room_number;
			}
			item->current_anim_state = item->goal_anim_state = TRAP_SET;
			item->anim_number = objects[item->object_number].anim_index;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = item->goal_anim_state = anims[item->anim_number].current_anim_state;
			item->required_anim_state = 0;
			RemoveActiveItem( item_number );
		}
	}
}

/******************************************************************************
 *				Rolling Ball Collision
 *****************************************************************************/
void	RollingBallCollision( sint16 item_num, ITEM_INFO *litem, COLL_INFO *coll )
{
	int			x,y,z,i;
	sint16		d;
	ITEM_INFO	*item;

	item = &items[ item_num ];

	// If Rolling Ball actually is rolling
	if ( item->status==ACTIVE )
	{
		// Exit now if Havent Collided with Ball..
		if ( !TestBoundsCollide( item,litem,coll->radius ) )
			return;
		if ( !TestCollision( item, litem ) )
			return;

		// If Lara is In Air only take a bit of damage and push her around
		if ( litem->gravity_status )
		{
			if ( coll->enable_baddie_push )
				ItemPushLara( item, litem, coll, coll->enable_spaz,1 );
			litem->hit_points -= ROLLINGBALL_DAMAGE_AIR;
			x = litem->pos.x_pos - item->pos.x_pos;
			y = (litem->pos.y_pos - 350) - (item->pos.y_pos-WALL_L/2);
			z = litem->pos.z_pos - item->pos.z_pos;
			d = (sint16)phd_sqrt(x*x + y*y + z*z);
			if ( d<WALL_L/2 )
				d = WALL_L/2;
			x = item->pos.x_pos + (x*WALL_L/2)/d;
			y = item->pos.y_pos - WALL_L/2 + (y*WALL_L/2)/d;
			z = item->pos.z_pos + (z*WALL_L/2)/d;
			DoBloodSplat( x,y,z, item->speed, item->pos.y_rot, item->room_number );
		}
		// If Lara is on Ground when Rolling ball hits her Run her Over...
		else
		{
			litem->hit_status = 1;
			if ( litem->hit_points>0 )				// Only attempt to Kill once
			{
				litem->hit_points = -1;
				//if (litem->room_number != item->room_number)
				//	ItemNewRoom(lara.item_number, item->room_number);
				litem->pos.y_rot = item->pos.y_rot;
				litem->pos.x_rot = litem->pos.z_rot = 0;

				litem->anim_number = RBALL_DEATH_A;				// Kick Off Rolling Ball
				litem->frame_number = RBALL_DEATH_F;     		// animation in extras
				litem->current_anim_state = AS_SPECIAL;
				litem->goal_anim_state = AS_SPECIAL;

				camera.flags = FOLLOW_CENTRE;
				camera.target_angle = 170*ONE_DEGREE;
				camera.target_elevation = -25*ONE_DEGREE;
				for ( i=0;i<15;i++ )
				{
					x = litem->pos.x_pos + (GetRandomControl()-16384)/256;
					z = litem->pos.z_pos + (GetRandomControl()-16384)/256;
					y = litem->pos.y_pos - GetRandomControl()/64;        // 512 random range...
					d = (GetRandomControl()-16384)/8 + item->pos.y_rot;
					DoBloodSplat( x,y,z, (sint16)(item->speed*2), d, item->room_number );
				}
			}
		}
	}
	else if ( item->status!=INVISIBLE )
		ObjectCollision( item_num,litem,coll );
}


/******************************************************************************
 *			Collision routine for Spikes
 *****************************************************************************/
void	SpikeCollision( sint16 item_num, ITEM_INFO *litem, COLL_INFO *coll )
{
	int			x,y,z,num;
	ITEM_INFO	*item;

	item = &items[ item_num ];
	if ( litem->hit_points<0 )
		return;

	if ( !TestBoundsCollide( item,litem,coll->radius ) )
		return;
	if ( !TestCollision( item, litem ) )
		return;

	num=GetRandomControl()/24576;
	if ( litem->gravity_status ) 	       //If in AIR and falling kill Lara straight away
	{                                      // else take off a little damage
		if ( litem->fallspeed>6 )		// SONY BUG FIX B14 - For when Lara is standing on a platform with spikes on it, then runs off the edge (used to kill her immediately and teleport)
		{
			litem->hit_points = -1;
			num = 20;
		}
	}
	else if ( litem->speed<30 )
		return;

	litem->hit_points -= SPIKE_DAMAGE;
	for ( ;num>0;num-- )
	{
		x = litem->pos.x_pos + (GetRandomControl()-16384)/256;
		z = litem->pos.z_pos + (GetRandomControl()-16384)/256;
		y = litem->pos.y_pos - GetRandomControl()/64;
		DoBloodSplat( x,y,z,20,(sint16)GetRandomControl(), item->room_number );
	}
	if ( litem->hit_points<=0 )
	{
		litem->anim_number = SPIKE_DEATH_A;				// Kick Off Rolling Ball
		litem->frame_number = SPIKE_DEATH_F;     		// animation in extras
		litem->current_anim_state = AS_DEATH;
		litem->goal_anim_state = AS_DEATH;
		litem->pos.y_pos = item->pos.y_pos;
		litem->gravity_status = 0;
	}
}



/******************************************************************************
 *							Trap Doors
 *****************************************************************************/
void	TrapDoorControl(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	if (TriggerActive(item))
	{
		if (item->current_anim_state == DOOR_CLOSED)
			item->goal_anim_state = DOOR_OPEN;
	}
	else
	{
		if (item->current_anim_state == DOOR_OPEN)
			item->goal_anim_state = DOOR_CLOSED;
	}

	AnimateItem(item);
}

void	TrapDoorFloor(ITEM_INFO *item, sint32 x, sint32 y, sint32 z, sint32 *height)
{
	if ( !OnTrapDoor(item, x, z) )
		return;
	if (y <= item->pos.y_pos)
		if (item->current_anim_state == DOOR_CLOSED && item->pos.y_pos < *height)
			*height = item->pos.y_pos;
}

void	TrapDoorCeiling(ITEM_INFO *item, sint32 x, sint32 y, sint32 z, sint32 *height)
{
	if ( !OnTrapDoor(item, x, z) )
		return;
	if (y > item->pos.y_pos)
		if (item->current_anim_state == DOOR_CLOSED && item->pos.y_pos > *height)
			*height = item->pos.y_pos + STEP_L;
}

int		OnTrapDoor(ITEM_INFO *item, sint32 x, sint32 z )
{
	int tx, tz;

	x >>= WALL_SHIFT;
	z >>= WALL_SHIFT;
	tx = item->pos.x_pos >> WALL_SHIFT;
	tz = item->pos.z_pos >> WALL_SHIFT;

	if (item->pos.y_rot == 0 && x==tx && (z==tz || z==tz+1))
		return (1);
	else if (item->pos.y_rot == -0x8000 && x==tx && (z==tz || z==tz-1))
		return (1);
	else if (item->pos.y_rot == 0x4000 && z==tz && (x==tx || x==tx+1))
		return (1);
	else if (item->pos.y_rot == -0x4000 && z==tz && (x==tx || x==tx-1))
		return (1);

	return (0);
}

/******************************************************************************
 *						Pendulum control...
 * Hierarchy.. PIN,BAR,BLADE
 *****************************************************************************/
void	Pendulum(sint16 item_number)
{
	int			x,y,z;
	sint16		d;
	ITEM_INFO 	*item;
	FLOOR_INFO *floor;

	item = &items[item_number];

#ifdef OLD_PENDULUM
	/* Pendulums can get switched on and off */
	if (TriggerActive(item))
	{
		if (item->current_anim_state == TRAP_SET)
			item->goal_anim_state = TRAP_WORKING;
	}
	else
	{
		if (item->current_anim_state == TRAP_WORKING)
			item->goal_anim_state = TRAP_SET;
	}

	if ( item->current_anim_state==TRAP_WORKING && item->touch_bits )
#endif
	/* New versions of 'pendulums' in Tomb2 don't get switched on/off, just swing and hurt */
	if (item->touch_bits)
	{
		lara_item->hit_points -= 50;
		lara_item->hit_status = 1;
		x = lara_item->pos.x_pos + (GetRandomControl()-16384)/256;
		z = lara_item->pos.z_pos + (GetRandomControl()-16384)/256;
		y = lara_item->pos.y_pos - GetRandomControl()/44;        // 750 random range...
		d = (GetRandomControl()-16384)/8 + lara_item->pos.y_rot;
		DoBloodSplat( x,y,z, lara_item->speed, d, lara_item->room_number );
	}

	/* Shadow height */
	floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &item->room_number);
	item->floor = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);

	AnimateItem(item);
}

/******************************************************************************
 *			Falling Floor Block Control routine
 *****************************************************************************/
void	FallingBlock(sint16 item_number)
{
	/* Falling floor block: FALLING_FLOOR3 is the planks */
	ITEM_INFO 	*item;
	FLOOR_INFO 	*floor;
	sint16 		room_number;
	sint32 origin;

	item = &items[item_number];

	if (item->object_number == FALLING_PLANK)
		origin = WALL_L;
	else
		origin = STEP_L*2;

	switch (item->current_anim_state)
	{
		case TRAP_SET:
			/* May not be activated (Lara needs to tread on block) */
			if (lara_item->pos.y_pos == item->pos.y_pos - origin)
				item->goal_anim_state = TRAP_ACTIVATE;
			else
			{
				item->status = NOT_ACTIVE;
				RemoveActiveItem( item_number );
				return;
			}
			break;

		case TRAP_ACTIVATE:
			/* Block is now Wobbling ready to fall */
			item->goal_anim_state = TRAP_WORKING;
			break;

		case TRAP_WORKING:
			/* Block has finished wobbling so now make it fall */
			if ( item->goal_anim_state!=TRAP_FINISHED )
				item->gravity_status = 1;
			break;
	}

	AnimateItem(item);
	if ( item->status==DEACTIVATED )
	{
		RemoveActiveItem(item_number);
		return;
	}

	room_number = item->room_number;
	floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
	if (item->room_number != room_number)
		ItemNewRoom(item_number, room_number);

	item->floor = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);

	/* Check for crashing block against the floor */
	if (item->current_anim_state == TRAP_WORKING && item->pos.y_pos >= item->floor)
	{
		item->goal_anim_state = TRAP_FINISHED;
		item->pos.y_pos = item->floor;
		item->fallspeed = 0;
		item->gravity_status = 0;
	}
}

void FallingBlockFloor(ITEM_INFO *item, sint32 x, sint32 y, sint32 z, sint32 *height)
{
	sint32 origin;

	if (item->object_number == FALLING_PLANK)
		origin = WALL_L;
	else
		origin = STEP_L*2;

	if ( y <= item->pos.y_pos - origin)
		if ( item->current_anim_state==TRAP_SET || item->current_anim_state==TRAP_ACTIVATE )
			*height = item->pos.y_pos - origin;
}

void FallingBlockCeiling(ITEM_INFO *item, sint32 x, sint32 y, sint32 z, sint32 *height)
{
	sint32 origin;

	if (item->object_number == FALLING_PLANK)
		origin = WALL_L;
	else
		origin = STEP_L*2;

	if ( y>item->pos.y_pos - origin)
		if ( item->current_anim_state==TRAP_SET || item->current_anim_state==TRAP_ACTIVATE )
			*height = item->pos.y_pos + STEP_L - origin;
}

/******************************************************************************
 *					Teeth Trap control routine
 *****************************************************************************/
	enum	tt_states { TT_NICE,TT_NASTY };
	BITE_INFO	teeth1a = { -23,0,-1718,0 };
	BITE_INFO	teeth1b = {  71,0,-1718,1 };
	BITE_INFO	teeth2a = { -23,10,-1718,0 };
	BITE_INFO	teeth2b = {  71,10,-1718,1 };
	BITE_INFO	teeth3a = { -23,-10,-1718,0 };
	BITE_INFO	teeth3b = {  71,-10,-1718,1 };

void TeethTrap(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	if ( TriggerActive(item) )
	{
		item->goal_anim_state = TT_NASTY;
		if ( item->touch_bits && item->current_anim_state==TT_NASTY )
		{
			lara_item->hit_points -= TEETHTRAP_DAMAGE;
			lara_item->hit_status = 1;
			BaddieBiteEffect( item, &teeth1a );
			BaddieBiteEffect( item, &teeth1b );
			BaddieBiteEffect( item, &teeth2a );
			BaddieBiteEffect( item, &teeth2b );
			BaddieBiteEffect( item, &teeth3a );
			BaddieBiteEffect( item, &teeth3b );
		}
	}
	else
		item->goal_anim_state = TT_NICE;

	AnimateItem(item);
}


/******************************************************************************
 *				Falling Ceiling control routine
 *****************************************************************************/
void FallingCeiling(sint16 item_number)
{
	ITEM_INFO *item;
	FLOOR_INFO *floor;
	sint16 room_number;

	item = &items[item_number];

	if (item->current_anim_state == TRAP_SET)
	{
		item->goal_anim_state = TRAP_ACTIVATE;
		item->gravity_status = 1;
	}
	else if (item->current_anim_state == TRAP_ACTIVATE)
	{
		if (item->touch_bits)
		{
			lara_item->hit_points -= FALLING_CEILING_DAMAGE;
			lara_item->hit_status = 1;
		}
	}

	AnimateItem(item);
	if ( item->status==DEACTIVATED )
	{
		RemoveActiveItem(item_number);
		return;
	}

	room_number = item->room_number;
	floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
	item->floor = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	if (room_number != item->room_number)
		ItemNewRoom(item_number, room_number);

	if (item->current_anim_state == TRAP_ACTIVATE && item->pos.y_pos >= item->floor)
	{
		item->goal_anim_state = TRAP_WORKING;
		item->pos.y_pos = item->floor;
		item->fallspeed = 0;
		item->gravity_status = 0;
	}
}

/********************************************************************
 *				Dart Emitter Controller
 *******************************************************************/

void	DartEmitterControl( sint16 item_num )
{
	ITEM_INFO		*item,*dart;
	sint16		dart_num;
	sint32		x,z,lp;

	item = &items[item_num];

	if (item->active)
	{
		if (item->timer <= 0)
			item->timer = 30;
		else
		{
			item->timer--;
			return;
		}
	}

	dart_num = CreateItem();
	if ( dart_num!=NO_ITEM )
	{
		dart = &items[dart_num];

		dart->object_number = DARTS;
		dart->room_number = item->room_number;
		dart->pos.y_rot = item->pos.y_rot;
		dart->pos.y_pos = item->pos.y_pos - WALL_L/2;
		x = z = 0;
		switch ( dart->pos.y_rot )      		// Setup Position of Dart
		{
			case 0:
				z = -WALL_L/2 + 100;
			break;

			case 16384:
				x = -WALL_L/2 + 100;
			break;

			case -32768:
				z = WALL_L/2 - 100;
			break;

			case -16384:
				x = WALL_L/2 - 100;
			break;
		}
		dart->pos.x_pos = item->pos.x_pos + x;
		dart->pos.z_pos = item->pos.z_pos + z;
		x<<=3;
		z<<=3;
		for (lp=0;lp<7;lp++)
			TriggerDartSmoke(dart->pos.x_pos, dart->pos.y_pos, dart->pos.z_pos, x>>lp, z>>lp, 0);
		InitialiseItem( dart_num );
		AddActiveItem( dart_num );										// Make It Active for Control
		dart->status = ACTIVE;

		SoundEffect(254, &dart->pos, 0);
	}
}



/********************************************************************
 *		Darts Controller
 *******************************************************************/
void	DartsControl( sint16 item_num )
{
	FX_INFO		*fx;
	ITEM_INFO	*item;
	FLOOR_INFO	*floor;
	sint16		room_number,fx_num;
	long		angle,lp,ox,oy,oz;

	item = &items[item_num];

	if (item->touch_bits)
	{
		lara_item->hit_points -= 50;
		lara_item->hit_status = 1;
		DoBloodSplat(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, lara_item->speed, lara_item->pos.y_rot, lara_item->room_number);
		KillItem( item_num );
		return;
	}

	ox = item->pos.x_pos;
	oz = item->pos.z_pos;

	angle = (((item->pos.y_rot>>4))&4095)<<1;
	item->pos.z_pos += (rcossin_tbl[angle+1]*DART_SPEED) >> 12;
	item->pos.x_pos += (rcossin_tbl[angle]*DART_SPEED) >> 12;

	room_number = item->room_number;
	floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
	if (item->room_number != room_number)
		ItemNewRoom(item_num, room_number);
	item->floor = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	if ( item->pos.y_pos >= item->floor )
	{
		for (lp=0;lp<4;lp++)
			TriggerDartSmoke(ox, item->pos.y_pos, oz, 0, 0, 1);
		KillItem( item_num );
//		SoundEffect(258, &item->pos, 0);
	}
}

void SideFlameEmitterControl(sint16 item_number)
{
	ITEM_INFO *item;
	FX_INFO *fx;
	sint16 fx_number;

	item = &items[item_number];

	/* Make a flame while the trigger is active */
	if (TriggerActive(item))
	{
		if (item->data == NULL)
		{
			fx_number = CreateEffect(item->room_number);
			if (fx_number != NO_ITEM)
			{
				fx = &effects[fx_number];
				fx->pos.x_pos = item->pos.x_pos;
				fx->pos.y_pos = item->pos.y_pos;
				fx->pos.z_pos = item->pos.z_pos;
				fx->pos.y_rot = item->pos.y_rot;
				fx->frame_number = SIDE_FIRE;
				fx->object_number = FLAME;
				fx->counter = fx->flag1 = fx->flag2 = 0;	// flag1 = How long to fire, flag2 = how long 'til fire again.
			}

			/* Store fx_number in data pointer space (+1 so not NULL if effect 0) */
			item->data = (void *)(fx_number + 1);
		}
	}
	else if (item->data)
	{
		KillEffect( (sint16)((int)(item->data) - 1) );
		item->data = NULL;
	}
}

void FlameEmitterControl(sint16 item_number)
{
	ITEM_INFO *item;
	FX_INFO *fx;
	sint16 fx_number;

	item = &items[item_number];

	/* Make a flame while the trigger is active */
	if (TriggerActive(item))
	{
		if (item->data == NULL)
		{
			fx_number = CreateEffect(item->room_number);
			if (fx_number != NO_ITEM)
			{
				fx = &effects[fx_number];
				fx->pos.x_pos = item->pos.x_pos;
				fx->pos.y_pos = item->pos.y_pos;
				fx->pos.z_pos = item->pos.z_pos;
				fx->pos.y_rot = item->pos.y_rot;
				fx->frame_number = BIG_FIRE;
				fx->object_number = FLAME;
				fx->counter = 0;
			}

			/* Store fx_number in data pointer space (+1 so not NULL if effect 0) */
			item->data = (void *)(fx_number + 1);
		}
	}
	else if (item->data)
	{
		KillEffect( (sint16)((int)(item->data) - 1) );
		item->data = NULL;
	}
}

void FlameEmitter2Control(sint16 item_number)
{

// Small fire.

	ITEM_INFO *item;
	FX_INFO *fx;
	sint16 fx_number;

	item = &items[item_number];

	/* Make a flame while the trigger is active */
	if (TriggerActive(item))
	{
		if (item->data == NULL)
		{
			fx_number = CreateEffect(item->room_number);
			if (fx_number != NO_ITEM)
			{
				fx = &effects[fx_number];
				fx->pos.x_pos = item->pos.x_pos;
				fx->pos.y_pos = item->pos.y_pos;
				fx->pos.z_pos = item->pos.z_pos;
				fx->pos.y_rot = item->pos.y_rot;
				fx->frame_number = SMALL_FIRE;
				fx->object_number = FLAME;
				fx->counter = 0;
			}

			/* Store fx_number in data pointer space (+1 so not NULL if effect 0) */
			item->data = (void *)(fx_number + 1);
		}
	}
	else if (item->data)
	{
		KillEffect( (sint16)((int)(item->data) - 1) );
		item->data = NULL;
	}
}


void FlameEmitter3Control(sint16 item_number)
{

// Random burst over 1 block flame.

	ITEM_INFO *item;
	FX_INFO *fx;
	sint16 fx_number;

	item = &items[item_number];

	/* Make a flame while the trigger is active */
	if (TriggerActive(item))
	{
		if (item->data == NULL)
		{
			fx_number = CreateEffect(item->room_number);
			if (fx_number != NO_ITEM)
			{
				fx = &effects[fx_number];
				fx->pos.x_pos = item->pos.x_pos;
				fx->pos.y_pos = item->pos.y_pos;
				fx->pos.z_pos = item->pos.z_pos;
				fx->frame_number = JET_FIRE;
				fx->object_number = FLAME;
				fx->counter = fx->flag1 = 0;
				fx->flag2 = GetRandomControl()&63;
			}

			/* Store fx_number in data pointer space (+1 so not NULL if effect 0) */
			item->data = (void *)(fx_number + 1);
		}
	}
	else if (item->data)
	{
		KillEffect( (sint16)((int)(item->data) - 1) );
		item->data = NULL;
	}
}

void FlameControl(sint16 fx_number)
{
	FX_INFO *fx;
	sint32 x, y, z, r, g, ox,oz, od, distance;
	unsigned char	xzoffs[16][2] = {	9,9,
								24,9,
								40,9,
								55,9,
								9,24,
								24,24,
								40,24,
								55,24,
								9,40,
							    	24,40,
								40,40,
								55,40,
								9,55,
								24,55,
								40,55,
								55,55 	};

//	fx->frame_number = type of flame... (0 = normal(fire), 1 = small fire, 2 = random bursts).

	fx = &effects[fx_number];

	distance = GetRandomControl();
	if (fx->frame_number == BIG_FIRE)
	{
  		TriggerStaticFlame(fx->pos.x_pos+x, fx->pos.y_pos, fx->pos.z_pos+z,g);
		if ((wibble&12))
		{
			g = (GetRandomControl()&15)+32;
			TriggerFireFlame(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos,-1,0);
			TriggerFireSmoke(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos,-1,0);
		}
	}
	else if (fx->frame_number == SMALL_FIRE)
	{
		r = ((fx->pos.y_rot>>4)&4095)<<1;
		z = (rcossin_tbl[r+1]*288) >> 12;
		x = (rcossin_tbl[r]*288) >> 12;

		g = (GetRandomControl()&15)+32;
  		TriggerStaticFlame(fx->pos.x_pos+x, fx->pos.y_pos-192, fx->pos.z_pos+z,g);

		if ((wibble&24)==0)
			TriggerFireFlame(fx->pos.x_pos+x, fx->pos.y_pos-224, fx->pos.z_pos+z,-1,1);
		if ((wibble&24)==0)
			TriggerFireSmoke(fx->pos.x_pos+x, fx->pos.y_pos, fx->pos.z_pos+z,-1,1);
	}
	else if (fx->frame_number == JET_FIRE)
	{
		if (fx->flag1 == 0)
		{
			fx->flag1 = (GetRandomControl()&3)+8; // How long the burst is.

			x = GetRandomControl()&63;		// Which hole to use (0-15).
			if (fx->flag2 == x)
				x = (x+13)&63;
			fx->flag2 = x;
		}
		else
			fx->flag1--;

		x = (xzoffs[fx->flag2&7][0]<<4) - 512;
		z = (xzoffs[fx->flag2&7][1]<<4) - 512;
		if ((wibble&4)==0)
			TriggerFireFlame(fx->pos.x_pos+x, fx->pos.y_pos, fx->pos.z_pos+z,-1,2);
		x = (xzoffs[(fx->flag2>>3)+8][0]<<4) - 512;
		z = (xzoffs[(fx->flag2>>3)+8][1]<<4) - 512;
		if (wibble&4)
			TriggerFireFlame(fx->pos.x_pos+x, fx->pos.y_pos, fx->pos.z_pos+z,-1,2);
	}
	else if (fx->frame_number == SIDE_FIRE)
	{
		r = ((fx->pos.y_rot>>4)&4095)<<1;
		oz = (rcossin_tbl[r+1]*(512+(distance&255))) >> 12;
		ox = (rcossin_tbl[r]*(512+(distance&255))) >> 12;

		if (fx->flag2)
		{
			if (wibble&4)
		    		TriggerSideFlame(fx->pos.x_pos+ox, fx->pos.y_pos, fx->pos.z_pos+oz,(r+4096)&8191, ((GetRandomControl()&7)==0), 1);
			fx->flag2--;
		}
		else
		{
			if (fx->flag1)
			{
				if (wibble & 4)
				{
					if (fx->flag1 > 112)
						TriggerSideFlame(fx->pos.x_pos+ox, fx->pos.y_pos, fx->pos.z_pos+oz,(r+4096)&8191, (129-fx->flag1)>>1, 0);
					else if (fx->flag1 < 18)
						TriggerSideFlame(fx->pos.x_pos+ox, fx->pos.y_pos, fx->pos.z_pos+oz,(r+4096)&8191, (fx->flag1>>1)+1, 0);
					else
						TriggerSideFlame(fx->pos.x_pos+ox, fx->pos.y_pos, fx->pos.z_pos+oz,(r+4096)&8191, 9, 0);
				}
				fx->flag1--;
			}
			else
			{
				fx->flag1 = 128;
				fx->flag2 = 48+(GetRandomControl()&15);
			}
		}
	}

	x = fx->pos.x_pos + ((distance & 0xf) << 5);
	y = fx->pos.y_pos + ((distance & 0xf0) << 1);
	z = fx->pos.z_pos + ((distance & 0xf00) >> 3);

	if (fx->frame_number == SIDE_FIRE)
	{
		if (fx->flag2 || fx->flag1 < 18)
			od = 0;
		else if (fx->flag1<64)				// Correct bit for after E3.
			od = 9*128;
		else
			od = (128-fx->flag1)<<5;

		r = (((fx->pos.y_rot>>4)+2048)&4095)<<1;
		oz = (rcossin_tbl[r+1]*od) >> 12;
		ox = (rcossin_tbl[r]*od) >> 12;
		r = 24+(distance&7);
		g = 12+((distance>>4)&3);
		TriggerDynamic(x+ox, y, z+oz, (fx->flag2 == 0) ? 13 : 6, r, g, 0);
	}
	else
	{
		r = 24+(distance&7);
		g = 12+((distance>>4)&3);
		TriggerDynamic(x, y, z, 16-(fx->frame_number<<2), r, g, 0);
	}


	/* If counter is < 0 this is a Lara burn flame. */

	if (fx->counter < 0)
	{
		fx->pos.x_pos = 0;
		fx->pos.y_pos = 0;
		if (fx->counter == -1)
			fx->pos.z_pos = -100;
		else
			fx->pos.z_pos = 0;
		GetJointAbsPosition(lara_item, (PHD_VECTOR *)&fx->pos, -fx->counter-1);
		if (lara_item->room_number != fx->room_number)
			EffectNewRoom(fx_number, lara_item->room_number);

		/* Put out by water */
		y = GetWaterHeight(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, fx->room_number);
		if (y != NO_HEIGHT && fx->pos.y_pos > y)
		{
			fx->counter = 0;
			KillEffect(fx_number);
			lara.burn = 0;
		}
		else
		{
			SoundEffect(150, &fx->pos, 0);
			lara_item->hit_points -= FLAME_ONFIRE_DAMAGE;
			lara_item->hit_status = 1;
		}
		return;
	}

	/* If fixed flame, reset counter (so Lara doesn't get loads of flames on herself) */

	SoundEffect(150, &fx->pos, 0);
	if (fx->counter)
		fx->counter--;
	else if (fx->frame_number != SMALL_FIRE)	// Torch flame?
	{
		PHD_3DPOS pos;

		pos.x_pos = fx->pos.x_pos;
		pos.y_pos = fx->pos.y_pos;
		pos.z_pos = fx->pos.z_pos;
		if (fx->frame_number == SIDE_FIRE)
		{
			pos.x_pos += ox;
			pos.z_pos += oz;
		}

		/* Check to see if Lara is caught by flame */
		if (ItemNearLara(&pos, 600))
		{
			x = lara_item->pos.x_pos - pos.x_pos;
			z = lara_item->pos.z_pos - pos.z_pos;
			distance = x*x + z*z;

			lara_item->hit_points -= FLAME_TOONEAR_DAMAGE;
			lara_item->hit_status = 1;

			if (distance < SQUARE(450))
			{
				/* Counter to stop Lara having flames started on her every frame */
				fx->counter = 100;

				/* If Lara caught by flame she catches fire! */
				LaraBurn();
			}
		}
	}
}

void LaraBurn(void)
{
	sint16 fx_number;
	FX_INFO *fx;

	if (lara.burn) // not multiple flames
		return;

	fx_number = CreateEffect(lara_item->room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->frame_number = 0;
		fx->object_number = FLAME;
		fx->counter = -1;
		lara.burn = 1;
	}
}


void LavaBurn(ITEM_INFO *item)
{
	/* If Lara steps into the LAVA she catches fire and dies */
	int i;
	FX_INFO *fx;
	sint16 fx_number, room_number;
	FLOOR_INFO *floor;

	/* If already dead exit now */
	if (item->hit_points < 0)
		return;

	/* Actually, she may be safe up on a bridge - better check the true floor height */
	room_number = item->room_number;
	floor = GetFloor(item->pos.x_pos, +32000, item->pos.z_pos, &room_number);
	if (item->floor != GetHeight(floor, item->pos.x_pos, +32000, item->pos.z_pos))
		return;
	item->hit_points = -1;
	item->hit_status = 1;
	for (i=0; i<10; i++)
	{
		fx_number = CreateEffect(item->room_number);
		if (fx_number != NO_ITEM)
		{
			fx = &effects[fx_number];
			fx->frame_number = objects[FLAME].nmeshes * GetRandomControl()/0x7fff;
			fx->object_number = FLAME;
			fx->counter = -GetRandomControl() * 24/0x7fff - 1;
		}
	}
}


void LavaSpray(sint16 item_number)
{
	/* Pretty plume of lava globs */
	ITEM_INFO *item;
	sint16 fx_number;
	FX_INFO *fx;

	item = &items[item_number];

	/* Create splots of Lava and fire them off randomly */
	fx_number = CreateEffect(item->room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->pos.x_pos = item->pos.x_pos;
		fx->pos.y_pos = item->pos.y_pos;
		fx->pos.z_pos = item->pos.z_pos;
		fx->pos.y_rot = (GetRandomControl() - 0x4000)*2;
		fx->speed =(sint16) GetRandomControl() >> 10;
		fx->fallspeed = -GetRandomControl() / 200;
		fx->frame_number = -GetRandomControl() * 4 / 0x7fff;
		fx->object_number = LAVA;

		SoundEffect(149, &item->pos, 0);
	}
}


void ControlLavaBlob(sint16 fx_number)
{
	FX_INFO *fx;
	FLOOR_INFO *floor;
	sint16 room_number;

	fx = &effects[fx_number];

	fx->pos.z_pos += fx->speed * phd_cos(fx->pos.y_rot) >> W2V_SHIFT;
	fx->pos.x_pos += fx->speed * phd_sin(fx->pos.y_rot) >> W2V_SHIFT;
	fx->fallspeed += GRAVITY;
	fx->pos.y_pos += fx->fallspeed;

	/* Absorbed by floor/ceiling */
	room_number = fx->room_number;
	floor = GetFloor(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, &room_number);
	if (fx->pos.y_pos >= GetHeight(floor, fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos) ||
		 fx->pos.y_pos < GetCeiling(floor, fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos))
	{
		KillEffect(fx_number);
		return;
	}

	/* Lara finds globs of molten lava painful */
	if (ItemNearLara(&fx->pos, 200))
	{
		lara_item->hit_points -= LAVA_GLOB_DAMAGE;
		lara_item->hit_status = 1;

		KillEffect(fx_number);
		return;
	}

	if (room_number != fx->room_number)
		EffectNewRoom(fx_number, room_number);
}

