#include "game.h"

/********** GLOBAL SOUND EFFECTS STUFF **************/
int number_sound_effects;
OBJECT_VECTOR *sound_effects;

#define FLIPFLAG   64
#define UNFLIPFLAG 128

int	AddAssaultTime(uint32 time);

int ItemNearLara(PHD_3DPOS *pos, sint32 distance)
{
	sint32 x, y, z;
	sint16 *bounds;

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

	/* Do linear test first (else squared distance may overflow!) */
	if (x < -distance || x > distance || z < -distance || z > distance || y < -WALL_L*3 || y > WALL_L*3)
		return (0);

	if (x*x + z*z > SQUARE(distance))
		return (0);

	bounds = GetBoundsAccurate(lara_item);
	if (y < bounds[2] || y > bounds[3]+100)
		return (0);

	return (1);
}


/*******************************************************************
 *				handle Ambient Sound Effects
 ******************************************************************/
void SoundEffects(void)
{
	int i;
	OBJECT_VECTOR *sound;

	//mn_reset_ambient_loudness();
	sound = sound_effects;
	for (i=number_sound_effects; i>0; i--, sound++)
	{
		/* Don't play sounds that are in other flipped version of the map
			NOTE: sounds that are playing are not stopped by this, but will play until they complete
					and not be renewed */
		if (!flip_status && (sound->flags & UNFLIPFLAG)==0)
			continue;
		else if (flip_status && (sound->flags & FLIPFLAG)==0)
			continue;

		SoundEffect(sound->data, (PHD_3DPOS *)&sound->x, 0);
	}

	/* Plus any flipeffect still in operation */
	if (flipeffect != -1)
		(*effect_routines[flipeffect])(NULL);

	//mn_update_sound_effects();
	SOUND_EndScene();		// finish sorting out looping sound effects
}

/*******************************************************************************************************
 *				Kick off a Blood Splat at a certain position
 ******************************************************************************************************/
sint16	DoBloodSplat( sint32 x, sint32 y, sint32 z, sint16 speed, PHD_ANGLE direction, sint16 room_num )
{
	FX_INFO		*fx;
	sint16		fx_num;

	if ( (fx_num=CreateEffect(room_num))!=NO_ITEM )
	{
		fx = &effects[fx_num];
		fx->pos.x_pos = x;
		fx->pos.y_pos = y;
		fx->pos.z_pos = z;
		fx->pos.y_rot = direction;
		fx->speed = speed;
		fx->frame_number = 0;
		fx->object_number = BLOOD1;
		fx->counter = 0;
	}
	return (fx_num);
}

#define BLOOD_RADIUS 256

void DoLotsOfBlood(sint32 x, sint32 y, sint32 z, sint16 speed, PHD_ANGLE direction, sint16 room_num, int number)
{
	/* Do several blood splats at roughly the position asked for */
	for ( ; number>0; number--)
		DoBloodSplat(x + BLOOD_RADIUS - (GetRandomDraw()*BLOOD_RADIUS*2/0x8000), 
						 y + BLOOD_RADIUS - (GetRandomDraw()*BLOOD_RADIUS*2/0x8000), 
						 z + BLOOD_RADIUS - (GetRandomDraw()*BLOOD_RADIUS*2/0x8000), 
						 speed, direction, room_num);
}

/******************************************************************************
 *			Control the Blood Splat Sprite
 *****************************************************************************/
void	ControlBlood1( sint16 fx_num )
{
	FX_INFO		*fx;

	fx = &effects[fx_num];
	fx->pos.x_pos += (phd_sin(fx->pos.y_rot) * fx->speed) >> W2V_SHIFT;
	fx->pos.z_pos += (phd_cos(fx->pos.y_rot) * fx->speed) >> W2V_SHIFT;
	fx->counter++;
	if ( fx->counter==4 )
	{
		fx->counter = 0;
		fx->frame_number--;
		if ( fx->frame_number<=objects[fx->object_number].nmeshes )
			KillEffect( fx_num );
	}
}

/******************************************************************************
 *			Control the Explosion Effect thingy...
 *****************************************************************************/
void	ControlExplosion1( sint16 fx_num )
{
	FX_INFO	*fx;

	fx = &effects[fx_num];
	fx->counter++;
	if ( fx->counter==2 )
	{
		fx->counter = 0;
		fx->frame_number--;
		if ( fx->frame_number<=objects[fx->object_number].nmeshes )
			KillEffect( fx_num );
		else
			AddDynamicLight(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, 13, 11);
	}
	else
		AddDynamicLight(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, 12, 10);
}

/********************************************************************
 *					Richochet Effect...
 *******************************************************************/
void	Richochet( GAME_VECTOR *pos )
{
	sint16			fx_num;
	FX_INFO			*fx;

	if ( (fx_num=CreateEffect(pos->room_number))!=NO_ITEM )
	{
		fx = &effects[fx_num];
		fx->pos.x_pos = pos->x;
		fx->pos.y_pos = pos->y;
		fx->pos.z_pos = pos->z;
		fx->counter = 4;
		fx->frame_number = (sint16)-( (int)(GetRandomDraw()*3)/32768 );
		fx->object_number = RICOCHET1;
		SoundEffect( 10, &fx->pos, 0 );		// play RICOCHET Sample
	}
}

/********************************************************************
 *				Control Richochet effect
 *******************************************************************/
void	ControlRichochet1( sint16 fx_num )
{
	effects[fx_num].counter--;					// Wait for Counter to
	if ( !effects[fx_num].counter )             // get to Zero before
		KillEffect( fx_num );                   // Killing Ricochet
}

/*******************************************************************
 *			Bubble Stuff
 ******************************************************************/

void CreateBubble(PHD_3DPOS *pos, sint16 room_number)
{
	sint16 fx_number;
	FX_INFO *fx;

	fx_number = CreateEffect(room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->pos.x_pos = pos->x_pos;
		fx->pos.y_pos = pos->y_pos;
		fx->pos.z_pos = pos->z_pos;
		fx->speed = 10 + ((GetRandomDraw()*6)>>15);
		fx->frame_number = -((GetRandomDraw()*3)>>15);
		fx->object_number = BUBBLES1;
	}
}


void	LaraBubbles( ITEM_INFO *item )
{
	PHD_VECTOR		offset;
	int				i;

	i = (GetRandomDraw()*3)/32768;
	if ( i )
	{
		SoundEffect( 37,&item->pos,UNDERWATER );
		offset.x = 0;
		offset.y = 0;
		offset.z = 50;
		GetJointAbsPosition( item,&offset,14 );     // Get Position of Head
		for ( ;i>0;i-- )
			CreateBubble((PHD_3DPOS *)&offset, item->room_number);
	}
}

/*****************************************************************************
 *				Control the Bubble
 ****************************************************************************/
void	ControlBubble1( sint16 fx_num )
{
	FX_INFO			*fx;
	OBJECT_INFO 	*object;
	FLOOR_INFO  	*floor;
	sint32			x,y,z,h;
	sint16			roomy;

	fx = &effects[fx_num];							// Bubbles Are Effects!!
	object = &objects[fx->object_number];

	fx->pos.y_rot += 9*ONE_DEGREE;
	fx->pos.x_rot += 13*ONE_DEGREE;
	x = fx->pos.x_pos + ((phd_sin(fx->pos.y_rot)*11)>>W2V_SHIFT);
	y = fx->pos.y_pos - fx->speed;
	z = fx->pos.z_pos + ((phd_cos(fx->pos.x_rot)*8)>>W2V_SHIFT);
	roomy = fx->room_number;

	floor = GetFloor( x,y,z,&roomy );
	if (!floor || !(room[roomy].flags&1) )		// If Bad floor or Not in Water KillEffect
	{
		KillEffect( fx_num );
		return;
	}
	h = GetCeiling( floor, x, y, z );			// If Ceiling is Bad or Bubble above Ceiling KillEffect
	if ( h==NO_HEIGHT || y<=h )
	{
		KillEffect( fx_num );
		return;
	}

	if ( fx->room_number!=roomy )
		EffectNewRoom( fx_num, roomy );
	fx->pos.x_pos = x;
	fx->pos.y_pos = y;
	fx->pos.z_pos = z;
}

/******************************************************************************
 *					Splash Effect for an Item....
 *****************************************************************************/
void	Splash( ITEM_INFO *item )
{
	int i;
	sint16 fx_number, room_number;
	sint32 height;
	FX_INFO *fx;

	height = GetWaterHeight(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, item->room_number);
	room_number = item->room_number;
	GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);

	for (i=10; i>0; i--)
	{
		fx_number = CreateEffect(room_number);
		if (fx_number != NO_ITEM)
		{
			fx = &effects[fx_number];
			fx->pos.x_pos = item->pos.x_pos;
			fx->pos.y_pos = height;
			fx->pos.z_pos = item->pos.z_pos;
			fx->pos.y_rot = (GetRandomDraw() - 0x4000)*2;
			fx->speed = GetRandomDraw() / 256;
			fx->frame_number = 0;
			fx->object_number = SPLASH1;
		}
	}
}


void	WadeSplash( ITEM_INFO *item, int water )
{
	sint16 fx_number, *bounds;
	FX_INFO *fx;

	return;

	if (item->speed == 0 && item->fallspeed == 0)
		return;

	bounds = GetBestFrame(item);

	if (item->pos.y_pos + bounds[2] > water || item->pos.y_pos + bounds[3] < water)
		return;

	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 = water;
		fx->pos.z_pos = item->pos.z_pos;
		fx->pos.y_rot = (GetRandomDraw() - 0x4000)*2;
		fx->speed = (sint16)(GetRandomDraw() >> 10);
		fx->frame_number = 0;
		fx->object_number = SPLASH1;
	}
}


void	ControlSplash1( sint16 fx_num )
{
	FX_INFO	*fx;

	fx = &effects[fx_num];
	fx->frame_number--;
	if (fx->frame_number<=objects[fx->object_number].nmeshes)
	{
		KillEffect(fx_num);
		return;
	}

	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;
}


void ControlWaterSprite(sint16 fx_num)
{
	FX_INFO	*fx;

	fx = &effects[fx_num];
	fx->counter--;
	if (!(fx->counter & 3))
	{
		fx->frame_number--;
		if (fx->frame_number<=objects[fx->object_number].nmeshes)
			fx->frame_number = 0;
	}

	if (!fx->counter || fx->fallspeed > 0)
	{
		KillEffect(fx_num);
		return;
	}

	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;
	if (fx->fallspeed)
	{
		fx->pos.y_pos += fx->fallspeed;
		fx->fallspeed += GRAVITY;
	}
}


void ControlSnowSprite(sint16 fx_num)
{
	FX_INFO	*fx;

	fx = &effects[fx_num];
	fx->frame_number--;
	if (fx->frame_number<=objects[fx->object_number].nmeshes)
	{
		KillEffect(fx_num);
		return;
	}

	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;
	if (fx->fallspeed)
	{
		fx->pos.y_pos += fx->fallspeed;
		fx->fallspeed += GRAVITY;
	}
}


void ControlHotLiquid(sint16 fx_number)
{
	FLOOR_INFO *floor;
	FX_INFO *fx;
	sint16 room_number;
	sint32 height;

	fx = &effects[fx_number];
	fx->frame_number--;
	if (fx->frame_number<=objects[HOT_LIQUID].nmeshes)
		fx->frame_number = 0;

	fx->pos.y_pos += fx->fallspeed;
	fx->fallspeed += GRAVITY;

	room_number = fx->room_number;
	floor = GetFloor(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, &room_number);
	height = GetHeight(floor, fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos);
	if (fx->pos.y_pos >= height)
	{
		SoundEffect(285, &fx->pos, 0);
		fx->object_number = SPLASH1;
		fx->pos.y_pos = height;
		fx->pos.y_rot = (sint16)(GetRandomDraw()<<1);
		fx->fallspeed = 0;
		fx->speed = 50;
		return;
	}
	else if (fx->room_number != room_number)
		EffectNewRoom(fx_number, room_number);
	SoundEffect(284, &fx->pos, 0);
}


#define WF_RANGE (WALL_L*10)

void WaterFall(sint16 item_number)
{
	ITEM_INFO *item;
	sint16 fx_number;
	FX_INFO *fx;
	sint32 x, y, z;

	item = &items[item_number];

	/* Only do its stuff if Lara is nearby */
	x = item->pos.x_pos - lara_item->pos.x_pos;
	y = item->pos.y_pos - lara_item->pos.y_pos;
	z = item->pos.z_pos - lara_item->pos.z_pos;

	if (x < -WF_RANGE || x > WF_RANGE || z < -WF_RANGE || z > WF_RANGE || y < -WF_RANGE || y > WF_RANGE)
		return;

	S_CalculateLight(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, item->room_number);

	fx_number = CreateEffect(item->room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->pos.x_pos = item->pos.x_pos + (GetRandomDraw() - 0x4000) * WALL_L/0x7fff;
		fx->pos.z_pos = item->pos.z_pos + (GetRandomDraw() - 0x4000) * WALL_L/0x7fff;
		fx->pos.y_pos = item->pos.y_pos;
		fx->speed = 0;
		fx->shade = (sint16)ls_adder;
		fx->frame_number = 0;
		fx->object_number = SPLASH1;
	}

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

/******************************************************************************
 ******************************************************************************
 *					Null Effect Routine
 ******************************************************************************
 *****************************************************************************/
void	void_effect( ITEM_INFO *item )
{
}
/******************************************************************************
 *					Flag Level is Complete
 *****************************************************************************/
void	finish_level_effect( ITEM_INFO *item )
{
	level_complete = 1;
}

/******************************************************************************
 *				Turn Current Item around 180 degrees
 *****************************************************************************/
void	turn180_effect( ITEM_INFO *item )
{
	item->pos.y_rot += -0x8000;
	item->pos.x_rot = -item->pos.x_rot;
}

/******************************************************************************
 *				Dinosaur stomp Camera effect
 *****************************************************************************/
#define MAX_BOUNCE	100
void	floor_shake_effect( ITEM_INFO *item )
{
	int		x,y,z,dist;

	x = item->pos.x_pos - camera.pos.x;
	y = item->pos.y_pos - camera.pos.y;
	z = item->pos.z_pos - camera.pos.z;
	if ( (ABS(x))<16*WALL_L && (ABS(y))<16*WALL_L && (ABS(z))<16*WALL_L )
	{
		dist = (x*x + y*y + z*z)/256;
		camera.bounce = ((WALL_L*WALL_L-dist)*MAX_BOUNCE)/(WALL_L*WALL_L);
	}
}

/******************************************************************************
 *				Return Lara to Stop after a Cinematic Cut etc
 *****************************************************************************/
void	lara_normal_effect( ITEM_INFO *item )
{
	item->current_anim_state = AS_STOP;
	item->goal_anim_state = AS_STOP;
	item->frame_number = STOP_F;
	item->anim_number = STOP_A;
	camera.type = CHASE_CAMERA;
	AlterFOV( GAME_FOV*ONE_DEGREE );
}


/******************************************************************************
 *				Do FlipMap Effects
 *****************************************************************************/
void BoilerFX( ITEM_INFO *item )
{
	SoundEffect(338, NULL, 0);
	flipeffect = -1;
}

void FloodFX(ITEM_INFO *item)
{
	PHD_3DPOS pos;

	// Waterfall sound: 79
	if (fliptimer > 30*4)
		flipeffect = -1;
	else
	{
		// Fire the sound effect up into the sky to make it fade off!! 
		pos.x_pos = lara_item->pos.x_pos;
		if (fliptimer < 30)
			pos.y_pos = camera.target.y + (30-fliptimer) * 100;
		else
			pos.y_pos = camera.target.y + (fliptimer-30)*100;
		pos.z_pos = lara_item->pos.z_pos;

		SoundEffect(79, &pos, 0);
	}
	fliptimer++;
}

void RubbleFX(ITEM_INFO *item)
{
	// Rubble falling into hold
	SoundEffect(24, NULL, 0);
	camera.bounce = -350;
	flipeffect = -1;
}

void ChandelierFX(ITEM_INFO *item)
{
	// Chandeliers going up and down
	SoundEffect(278, NULL, 0);
	fliptimer++;
	if (fliptimer>30)
		flipeffect = -1;
}

void ExplosionFX(ITEM_INFO *item)
{
	// EXPLOSION 105
	SoundEffect(105, NULL, 0);
	camera.bounce = -75;
	flipeffect = -1;
}

void PistonFX(ITEM_INFO *item)
{
	SoundEffect(190, NULL, 0);
	flipeffect = -1;
}

void CurtainFX(ITEM_INFO *item)
{
	SoundEffect(191, NULL, 0);
	flipeffect = -1;
}

void StatueFX(ITEM_INFO *item)
{
	SoundEffect(331, NULL, 0);
	flipeffect = -1;
}

void SetChangeFX(ITEM_INFO *item)
{
	SoundEffect(330, NULL, 0);
	flipeffect = -1;
}

/************* SOUND EFFECT STYLE OBJECTS *************/

void ControlDingDong(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	if ((item->flags & CODE_BITS) == CODE_BITS)
	{
		SoundEffect(334, &item->pos, 0);
		item->flags -= CODE_BITS;
	}
}

void ControlLaraAlarm(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];
	if ((item->flags & CODE_BITS) == CODE_BITS)
		SoundEffect(335, &item->pos, 0);
}

void ControlAlarmSound(sint16 item_number)
{
	int timer;
	ITEM_INFO *item;

	item = &items[item_number];
	if ((item->flags & CODE_BITS) == CODE_BITS)
	{
		SoundEffect(332, &item->pos, 0);

		timer = (int)item->data;
		timer++;
		if (timer > 6)
		{
			AddDynamicLight(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, 12, 11);
			if (timer > 12)
				timer = 0;
		}
		item->data = (void *)timer;
	}
}

void ControlBirdTweeter(sint16 item_number)
{
	// bird tweet AND water plip
	ITEM_INFO *item;

	item = &items[item_number];
	if (item->object_number == BIRD_TWEETER)
	{
		if (GetRandomDraw() < 0x400)
			SoundEffect(316, &item->pos, 0);
	}
	else if (GetRandomDraw() < 0x100)
		SoundEffect(329, &item->pos, 0);
}


void DoChimeSound(ITEM_INFO *item)
{
	// lie about position of bell tower to give effect of getting closer
	PHD_3DPOS pos;

	pos.x_pos = lara_item->pos.x_pos + ((item->pos.x_pos - lara_item->pos.x_pos) >> 6);
	pos.y_pos = lara_item->pos.y_pos + ((item->pos.y_pos - lara_item->pos.y_pos) >> 6);
	pos.z_pos = lara_item->pos.z_pos + ((item->pos.z_pos - lara_item->pos.z_pos) >> 6);

	SoundEffect(208, &pos, 0);
}

void ControlClockChimes(sint16 item_number)
{
	ITEM_INFO *item;
	
	item = &items[item_number];
	if (item->timer == 0)
		return;

	if ((item->timer % 60) == 59) // go for -1, else repeatedly sounds whilst on trigger space
		DoChimeSound(item);
	item->timer--;

	if (item->timer == 0)
	{
		DoChimeSound(item);
		item->timer = -1;
		RemoveActiveItem(item_number);
		item->status = NOT_ACTIVE;
		item->flags &= ~(CODE_BITS);
	}
}


#define SPHERE_RADIUS (STEP_L*5/2)

void SphereOfDoomCollision(sint16 item_number, ITEM_INFO *laraitem, COLL_INFO *coll)
{
	int radius, x, z, dist;
	ITEM_INFO *item;
	sint16 angle, diff;

	if (room[laraitem->room_number].flags & UNDERWATER)
		return;

	item = &items[item_number];

	radius = item->timer * SPHERE_RADIUS >> 8;
	x = laraitem->pos.x_pos - item->pos.x_pos;
	z = laraitem->pos.z_pos - item->pos.z_pos;
	dist = x*x + z*z;

	if (dist < radius*radius)
	{
		/* Whump! Been hit by sphere - throw Lara through air */
		angle = phd_atan(z, x);
		diff = laraitem->pos.y_rot - angle;
		if (ABS(diff) < 0x4000)
		{
			/* Facing away from blast */
			laraitem->speed = 150;
			laraitem->pos.y_rot = angle;
		}
		else
		{
			/* Facing towards blast */
			laraitem->pos.y_rot = (sint16)(angle-0x8000);
			laraitem->speed = -150;
		}
		laraitem->gravity_status = 1;
		laraitem->fallspeed = -50;
		laraitem->pos.z_pos = item->pos.z_pos + ((radius+50) * phd_cos(angle) >> W2V_SHIFT);
		laraitem->pos.x_pos = item->pos.x_pos + ((radius+50) * phd_sin(angle) >> W2V_SHIFT);
		laraitem->pos.x_rot = laraitem->pos.z_rot = 0;
		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;
	}
}

void SphereOfDoom(sint16 item_number)
{
	ITEM_INFO *item;
	PHD_3DPOS pos;
	int dist, x, y, z, radius;

	item = &items[item_number];

	item->timer+=64;
	item->pos.y_rot += (item->object_number == SPHERE_OF_DOOM2)? ONE_DEGREE*10 : -ONE_DEGREE*10;

	x = item->pos.x_pos - lara_item->pos.x_pos;
	y = item->pos.y_pos - lara_item->pos.y_pos;
	z = item->pos.z_pos - lara_item->pos.z_pos;

	radius = item->timer * SPHERE_RADIUS >> 8;
	dist = phd_sqrt(x*x + y*y + z*z) - radius;

	pos.x_pos = lara_item->pos.x_pos + x * dist / radius;
	pos.y_pos = lara_item->pos.y_pos + y * dist / radius; 
	pos.z_pos = lara_item->pos.z_pos + z * dist / radius; 
	SoundEffect(341, &pos, 0);

	if (item->timer > (30<<7))
		KillItem(item_number);
}

void DrawSphereOfDoom(ITEM_INFO *item)
{
	int scale, clip;
	sint16 *frame;

	phd_PushMatrix();
	phd_TranslateAbs(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	phd_RotY(item->pos.y_rot);

	/* Scale matrix according to current size of sphere */
	scale = item->timer;
	*(phd_mxptr+M00) = *(phd_mxptr+M00) * scale >> 8;
	*(phd_mxptr+M01) = *(phd_mxptr+M01) * scale >> 8;
	*(phd_mxptr+M02) = *(phd_mxptr+M02) * scale >> 8;
	*(phd_mxptr+M10) = *(phd_mxptr+M10) * scale >> 8;
	*(phd_mxptr+M11) = *(phd_mxptr+M11) * scale >> 8;
	*(phd_mxptr+M12) = *(phd_mxptr+M12) * scale >> 8;
	*(phd_mxptr+M20) = *(phd_mxptr+M20) * scale >> 8;
	*(phd_mxptr+M21) = *(phd_mxptr+M21) * scale >> 8;
	*(phd_mxptr+M22) = *(phd_mxptr+M22) * scale >> 8;

	frame = anims[item->anim_number].frame_ptr;
	clip = S_GetObjectBounds(frame);
	if (clip)
	{
		CalculateObjectLighting(item, frame);
		phd_PutPolygons(meshes[objects[item->object_number].mesh_index], clip);
	}

	phd_PopMatrix();
}

/******************************************************************************
 *			Free up Laras hands so she can use weapons
 *****************************************************************************/
void	lara_hands_free( ITEM_INFO *item )
{
	lara.gun_status = LG_ARMLESS;
}

/******************************************************************************
 *				Flip the Map effect for Cutscenes
 *****************************************************************************/
void	flip_map_effect( ITEM_INFO *item )
{
	FlipMap();
}

/******************************************************************************
 *				Draw Right Gun used In Cutscenes!!!
 *****************************************************************************/
void	draw_right_gun( ITEM_INFO *item )
{
	sint16	*temp;
//	OBJECT_INFO	*obj;

	temp = lara.mesh_ptrs[THIGH_R];
	lara.mesh_ptrs[THIGH_R] = meshes[objects[PISTOLS].mesh_index + THIGH_R];
	meshes[objects[PISTOLS].mesh_index + THIGH_R] = temp;
	temp = lara.mesh_ptrs[HAND_R];
	lara.mesh_ptrs[HAND_R] = meshes[objects[PISTOLS].mesh_index + HAND_R];
	meshes[objects[PISTOLS].mesh_index + HAND_R] = temp;
/*	obj = &objects[item->object_number];
	temp = meshes[obj->mesh_index + THIGH_R];
	meshes[ obj->mesh_index + THIGH_R] = meshes[objects[PISTOLS].mesh_index + THIGH_R];
	meshes[objects[PISTOLS].mesh_index + THIGH_R] = temp;
	temp = meshes[obj->mesh_index + HAND_R];
	meshes[obj->mesh_index + HAND_R] = meshes[objects[PISTOLS].mesh_index + HAND_R];
	meshes[objects[PISTOLS].mesh_index + HAND_R] = temp;
*/
}
void	draw_left_gun( ITEM_INFO *item )
{
	sint16	*temp;
//	OBJECT_INFO	*obj;

	temp = lara.mesh_ptrs[THIGH_L];
	lara.mesh_ptrs[THIGH_L] = meshes[objects[PISTOLS].mesh_index + THIGH_L];
	meshes[objects[PISTOLS].mesh_index + THIGH_L] = temp;
	temp = lara.mesh_ptrs[HAND_L];
	lara.mesh_ptrs[HAND_L] = meshes[objects[PISTOLS].mesh_index + HAND_L];
	meshes[objects[PISTOLS].mesh_index + HAND_L] = temp;
/*	obj = &objects[item->object_number];
	temp = meshes[obj->mesh_index + THIGH_L];
	meshes[ obj->mesh_index + THIGH_L] = meshes[objects[PISTOLS].mesh_index + THIGH_L];
	meshes[objects[PISTOLS].mesh_index + THIGH_L] = temp;
	temp = meshes[obj->mesh_index + HAND_L];
	meshes[obj->mesh_index + HAND_L] = meshes[objects[PISTOLS].mesh_index + HAND_L];
	meshes[objects[PISTOLS].mesh_index + HAND_L] = temp;
*/
}


void	shoot_right_gun( ITEM_INFO *item )
{
}
void	shoot_left_gun( ITEM_INFO *item )
{
}

void	nofunc( ITEM_INFO *item )
{
}

void	swap_meshes_with_meshswap1( ITEM_INFO *item )
{
	int	i;
	sint16	nmeshes;
	sint16	*temp;
	OBJECT_INFO	*obj;

	obj = &objects[item->object_number];
	nmeshes = obj->nmeshes;
	for( i=0; i<obj->nmeshes; i++ )
	{
		temp = meshes[obj->mesh_index + i];
		meshes[ obj->mesh_index + i] = meshes[objects[MESHSWAP1].mesh_index + i];
		meshes[objects[MESHSWAP1].mesh_index + i] = temp;
	}
}
void	swap_meshes_with_meshswap2( ITEM_INFO *item )
{
	int	i;
	sint16	nmeshes;
	sint16	*temp;
	OBJECT_INFO	*obj;

	obj = &objects[item->object_number];
	nmeshes = obj->nmeshes;
	for( i=0; i<obj->nmeshes; i++ )
	{
		temp = meshes[obj->mesh_index + i];
		meshes[ obj->mesh_index + i] = meshes[objects[MESHSWAP2].mesh_index + i];
		meshes[objects[MESHSWAP2].mesh_index + i] = temp;
	}
}

void	swap_meshes_with_meshswap3( ITEM_INFO *item )
{
	int	i;
	sint16	nmeshes;
	sint16	*temp;
	OBJECT_INFO	*obj;

	obj = &objects[item->object_number];
	nmeshes = obj->nmeshes;
	for( i=0; i<obj->nmeshes; i++ )
	{
		temp = meshes[obj->mesh_index + i];
		meshes[ obj->mesh_index + i] = meshes[objects[MESHSWAP3].mesh_index + i];
		if (item==lara_item)
			lara.mesh_ptrs[i] = meshes[objects[MESHSWAP3].mesh_index + i];
		meshes[objects[MESHSWAP3].mesh_index + i] = temp;
	}
}

void invisibility_on( ITEM_INFO *item )
{
	item->status = INVISIBLE;
}

void invisibility_off( ITEM_INFO *item )
{
	item->status = ACTIVE;
}

void dynamic_light_on( ITEM_INFO *item )
{
	item->dynamic_light = 1;
}

void dynamic_light_off( ITEM_INFO *item )
{
	item->dynamic_light = 0;
}

void reset_hair( ITEM_INFO *item )
{
	InitialiseHair();
}

/************* ASSAULT COURSE CLOCK **********************/

int assault_timer_display=0, assault_timer_active=0, assault_timer_best=-1;

void AssaultStart(ITEM_INFO *item)
{
	/* Set the clock to zero and display */
	savegame.timer = 0;
	assault_timer_active = 1;
	assault_timer_display = 1;
	flipeffect = -1;
}

void AssaultStop(ITEM_INFO *item)
{
	/* Stop the clock, but keep display */
	assault_timer_active = 0;
	assault_timer_display = 1;
	flipeffect = -1;
}

void AssaultReset(ITEM_INFO *item)
{
	/* Stop the clock, and remove from display */
	assault_timer_active = 0;
	assault_timer_display = 0;
	flipeffect = -1;
}

void AssaultFinished(ITEM_INFO *item)
{
	if (assault_timer_active)
	{
		AddAssaultTime( savegame.timer );

		if (assault_timer_best<0) // first go
		{
			if (savegame.timer < 100*30)
			{
				S_CDPlay(22, 0); // best
				assault_timer_best = savegame.timer;
			}
			else
			{
				S_CDPlay(24, 0); // first go
				assault_timer_best = 100*30; 
			}
		}
		else if (savegame.timer < (uint32)assault_timer_best)
		{
			S_CDPlay(22, 0); // best
			assault_timer_best = savegame.timer;
		}
		else if (savegame.timer < (uint32)assault_timer_best + (5*30))
			S_CDPlay(23, 0); // almost best
		else
			S_CDPlay(21, 0); // useless
		assault_timer_active = 0;
	}
	flipeffect = -1;
}

/******************************************************************************
 *					Effects Routines...
 *****************************************************************************/
void	(*effect_routines[])( ITEM_INFO *item ) = {
	turn180_effect,
	floor_shake_effect,
	lara_normal_effect,
	LaraBubbles,
	finish_level_effect,
	FloodFX, 
	ChandelierFX,
	RubbleFX,
	PistonFX,
	CurtainFX,
	SetChangeFX,
	ExplosionFX,
	lara_hands_free,
	flip_map_effect,
	draw_right_gun,
	draw_left_gun, //ChainBlockFX,
	shoot_right_gun, //FlickerFX
	shoot_left_gun,
	swap_meshes_with_meshswap1,
	swap_meshes_with_meshswap2,
	swap_meshes_with_meshswap3,
	invisibility_on,
	invisibility_off,
	dynamic_light_on,
	dynamic_light_off,
	StatueFX,
	reset_hair,
	BoilerFX,
	AssaultReset,
	AssaultStop,
	AssaultStart,
	AssaultFinished
};

