#include "game.h"

/*****************************************************************************
 *****************************************************************************
 *						FLARE STUFF
 *****************************************************************************
 ****************************************************************************/

#define MAX_FLARE_AGE	(30*30)
#define FLARE_YOUNG		(16)
#define FLARE_OLD		(MAX_FLARE_AGE - (3*30))
#define FLARE_DEAD		(MAX_FLARE_AGE - 24)

int DoFlareLight(PHD_VECTOR *pos, int flare_age)
{
	/* Adds the dynamic light to the flare of an intensity determined by the flare_age.
		Returns 1 if need the flame on the end, 0 if not */
	int x, y, z, random;
	long	r,g,b,falloff;

	if (flare_age >= MAX_FLARE_AGE)
		return 0;

	// randomize the position of the light to give a flicker
	random = GetRandomDraw();
	x = pos->x + ((random & 0xf) << 3);
	y = pos->y + ((random & 0xf0) << 1);
	z = pos->z + ((random & 0xf00) >> 5);

	if (flare_age < (FLARE_YOUNG>>2))
	{
		r = (flare_age<<1)+20+(random&3);
		g = flare_age+4+((random>>4)&3);
		b = flare_age<<1+((random>>8)&3);
		falloff = (flare_age<<2)+4+(random&3);
		if (falloff>16)
			falloff-=((random>>12)&3);
		TriggerDynamic(x,y,z,falloff,r,g,b);
	}
	else if (flare_age < FLARE_YOUNG)
	{
		r = (flare_age>>1)+16+(random&7);
		g = (flare_age>>1)+8+((random>>4)&3);
		b = (flare_age>>1)+2+((random>>8)&3);
		falloff = flare_age+2+(random&1);
		TriggerDynamic(x,y,z,falloff,r,g,b);
	}
	else if (flare_age < FLARE_OLD)
	{
		r = 24+(random&7);
		g = 16+((random>>4)&3);
		b = ((random>>8)&7)+(((random>>6)&16)>>2);	// Flicker purple once in a while.
		falloff = 16;
		TriggerDynamic(x,y,z,falloff,r,g,b);
	}
	else if (flare_age < FLARE_DEAD)
	{
		if (random > 0x2000)
		{
			r = 24+(random&7);
			g = 16+((random>>4)&3);
			b = ((random>>8)&7)+(((random>>6)&16)>>2);	// Flicker purple once in a while.
			falloff = 16;
			TriggerDynamic(x,y,z,falloff,r,g,b);
		}
		else
		{
			TriggerDynamic(x,y,z,(GetRandomControl()&6)+8,(GetRandomControl()&7)+24,(GetRandomControl()&7)+8,GetRandomControl()&15);
			return (0);
		}
	}
	else
	{
		TriggerDynamic(x,y,z,16-((flare_age-FLARE_DEAD)>>1),(GetRandomControl()&7)+24,(GetRandomControl()&7)+8,GetRandomControl()&3);
		return (random&1);
	}

	return (1);
}

void DoFlareInHand(int flare_age)
{
	/* Adds a dynamic light to the end of the flare and flags that the flare flame should be drawn.
		'flare_age' is a count of how long the flare has been lit */
	PHD_VECTOR pos;

	pos.x = 11;
	pos.y = 32;
	pos.z = 41;
	GetLaraJointAbsPosition(&pos,13);

	lara.left_arm.flash_gun = DoFlareLight(&pos, flare_age);

	if (lara.flare_age < MAX_FLARE_AGE)
	{
		lara.flare_age++;

		if (room[lara_item->room_number].flags & UNDERWATER)
		{
			SoundEffect(12, &lara_item->pos, UNDERWATER);
			if (GetRandomDraw() < 0x4000)
				CreateBubble((PHD_3DPOS *)&pos, lara_item->room_number);
		}
		else
			SoundEffect(12, &lara_item->pos, 0);
	}
	else if (lara.gun_status == LG_ARMLESS)
		lara.gun_status = LG_UNDRAW;
}

void DrawFlareInAir(ITEM_INFO *item)
{
	int 			clip, rate;
	OBJECT_INFO *object;
	sint16		*frmptr[2];
	long		xv,yv,zv,lp;

	GetFrames(item, frmptr, &rate);

	object = &objects[item->object_number];

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

	if ( (clip=S_GetObjectBounds(frmptr[0])) )
	{
		CalculateObjectLighting(item, frmptr[0]);
		phd_PutPolygons(meshes[objects[FLARE_ITEM].mesh_index], clip);

		if ((sint32)item->data & 0x8000)
		{
			phd_TranslateRel(-6,6,48);
			phd_RotX(-90*ONE_DEGREE);
//			phd_RotY((sint16)(GetRandomDraw()*2));
			S_CalculateStaticLight(8*256);
//			phd_PutPolygons(meshes[objects[FLARELIGHT].mesh_index], clip);
//#ifdef PSX_VERSION
			S_DrawSprite(SPRITE_REL|SPRITE_SEMITRANS|SPRITE_TRANS_ADD|SPRITE_SHADE|SPRITE_TINT|SPRITE_COLOUR(0xff,0x80,0x80),0,0,0,objects[GLOW].mesh_index,(GetRandomDraw()&0xfff)+0x1000,0);
//#endif
			phd_PushUnitMatrix();
			phd_RotYXZ(item->pos.y_rot, item->pos.x_rot, item->pos.z_rot);
			phd_PushMatrix();
			phd_TranslateRel(-6,6,32);
			xv = item->pos.x_pos + (phd_mxptr[M03]>>W2V_SHIFT);
			yv = item->pos.y_pos + (phd_mxptr[M13]>>W2V_SHIFT);
			zv = item->pos.z_pos + (phd_mxptr[M23]>>W2V_SHIFT);
			phd_PopMatrix();
			phd_TranslateRel((GetRandomDraw()&127)-64,(GetRandomDraw()&127)-64,(GetRandomDraw()&511)+512);
			for (lp=0;lp<(GetRandomDraw()&3)+4;lp++)
				TriggerFlareSparks(xv,yv,zv, (phd_mxptr[M03]>>W2V_SHIFT),(phd_mxptr[M13]>>W2V_SHIFT),(phd_mxptr[M23]>>W2V_SHIFT),lp>>2);
			phd_PopMatrix();
		}
	}
	phd_PopMatrix();
}


void CreateFlare(int thrown)
{
	sint16 flare_item;
	ITEM_INFO *flare;
	PHD_VECTOR	pos;
	FLOOR_INFO *floor;

	flare_item = CreateItem();
	if (flare_item != NO_ITEM)
	{
		flare = &items[flare_item];

		flare->object_number = FLARE_ITEM;
		flare->room_number = lara_item->room_number;
		pos.x = -16;
		pos.y = 32;
		pos.z = 42;
		GetLaraJointAbsPosition(&pos,13);

		/* Need to check this position is not inside a wall */
		floor = GetFloor(pos.x, pos.y, pos.z, &flare->room_number);
		if (GetHeight(floor, pos.x, pos.y, pos.z) < pos.y)
		{
			/* If the flare has been launched inside a wall, snap to Lara's coords instead */
			flare->pos.x_pos = lara_item->pos.x_pos;
			flare->pos.y_pos = pos.y;
			flare->pos.z_pos = lara_item->pos.z_pos;
			flare->pos.y_rot = lara_item->pos.y_rot;
			flare->room_number = lara_item->room_number;
		}
		else
		{
			/* Otherwise use the position at the end of her hand */
			flare->pos.x_pos = pos.x;
			flare->pos.y_pos = pos.y;
			flare->pos.z_pos = pos.z;
			if (thrown)
				flare->pos.y_rot = lara_item->pos.y_rot;
			else
				flare->pos.y_rot = lara_item->pos.y_rot - 0x2000; // slight angle if dropped
		}

		InitialiseItem(flare_item);

		flare->pos.z_rot = 0;
		flare->pos.x_rot = 0;
		flare->shade = -1;

		/* Starting speed depends on whether flare was thrown or dropped */
		if (thrown)
		{
			flare->speed = lara_item->speed + 50;
			flare->fallspeed = lara_item->fallspeed - 50;
		}
		else
		{
			flare->speed = lara_item->speed + 10;
			flare->fallspeed = lara_item->fallspeed + 50;
		}

		if (DoFlareLight((PHD_VECTOR *)&flare->pos, lara.flare_age))
			flare->data = (void *)(lara.flare_age | 0x8000);
		else
			flare->data = (void *)(lara.flare_age & 0x7fff);

		AddActiveItem(flare_item);
		flare->status = ACTIVE;
	}
}


void set_flare_arm(int frame)
{
	int anim_base;

	anim_base = objects[FLARE].anim_index;
	if (frame < FL_THROW_F)
		lara.left_arm.anim_number = anim_base;
	else if (frame < FL_DRAW_F)
		lara.left_arm.anim_number = anim_base+1;
	else if (frame < FL_IGNITE_F)
		lara.left_arm.anim_number = anim_base+2;
	else if (frame < FL_2HOLD_F)
		lara.left_arm.anim_number = anim_base+3;
	else
		lara.left_arm.anim_number = anim_base+4;

	lara.left_arm.frame_base = anims[lara.left_arm.anim_number].frame_ptr;
}

/*****************************************************************************
 *        Get flare from bag/belt
 ****************************************************************************/
void	draw_flare( void )
{
	sint16	ani;

	// Handle special pickup anim for thrown flare
	if (lara_item->current_anim_state == AS_FLAREPICKUP
		|| lara_item->current_anim_state == AS_PICKUP) // SECOND TEST IS TEMP UNTIL UNDERWATER FLARE PICKUP ANIM IS DONE
	{
		DoFlareInHand(lara.flare_age);
		lara.flare_control_left = 0;
		lara.left_arm.frame_number = FL_2HOLD_F-2; // HACK; allows it to drop through to HOLD anim when ready
		set_flare_arm(lara.left_arm.frame_number);
		return;
	}


	ani = lara.left_arm.frame_number;
	ani++;

	lara.flare_control_left = 1;

	if ( ani<FL_DRAW_F || ani>FL_2HOLD_F-1 )	// Test for out of bounds anim frames
		ani = FL_DRAW_F;
	else if ( ani==FL_DRAWGOTIT_F )	// Test for point at which flare is in Lara's hand
	{
		draw_flare_meshes();          // Put Flare in Hand
		if (!savegame.bonus_flag)
			Inv_RemoveItem( FLAREBOX_ITEM );
	}
	else if ( ani>=FL_IGNITE_F && ani<=(FL_2HOLD_F-2) )	// Add dynamic light while igniting flare
	{
		// initialise age of flare as it ignites
		if (ani==FL_IGNITE_F)
		{
			if (room[lara_item->room_number].flags & UNDERWATER)
				SoundEffect(11, &lara_item->pos, UNDERWATER);
			else
				SoundEffect(11, &lara_item->pos, 0);
			lara.flare_age = 0;
		}
		DoFlareInHand(lara.flare_age);
	}
	else if ( ani==(FL_2HOLD_F-1) )
	{
		ready_flare();                // Finished Draw
		ani = FL_HOLD_F;                 // so now ready to AIM
		DoFlareInHand(lara.flare_age);
	}

	// Now set the anim frame for each arm
	lara.left_arm.frame_number = ani;
	set_flare_arm(lara.left_arm.frame_number);
}

/*****************************************************************************
 *        Return flare to bag/belt
 ****************************************************************************/
void	undraw_flare( void )
{
	sint16	ani, ani2;

	ani = lara.left_arm.frame_number;
	ani2 = lara.flare_frame;

	// assume Lara is moving
	lara.flare_control_left = 1;	// left arm only

	if ( lara_item->goal_anim_state==AS_STOP && lara.skidoo==NO_ITEM)
	{
		// Lara is about to stop, so use full on flare throw anim from lara prj
		if ( lara_item->anim_number==BREATH_A )
		{
			// Use flare anim from LARA project
			//lara.left_arm.frame_base = lara.right_arm.frame_base = objects[LARA].frame_base;
			lara_item->anim_number = THROWFLARE_A;
			lara_item->frame_number = lara.flare_frame = ani2 = THROWFLARE_F+ani;
		}

		if ( lara_item->anim_number==THROWFLARE_A )
		{
			lara.flare_control_left = 0;
			if ( ani2>=THROWFLARE_F+FL_THROW_FRAMES-1 )
			{
				// Throw flare anim finished, so release Lara from flare control
				ani2 = 0;
				lara.gun_type = lara.request_gun_type = lara.last_gun_type;
				lara.gun_status = LG_ARMLESS;
				InitialiseNewWeapon();
				lara.target = NULL;
				lara.left_arm.lock = lara.right_arm.lock = 0;
				lara_item->anim_number = STOP_A;
				lara_item->frame_number = lara.flare_frame = STOP_F;
				lara_item->current_anim_state = AS_STOP;
				lara_item->goal_anim_state = AS_STOP;
				return;
			}
			// Keep updating 'arm only' anim frame counter in case Lara runs or something
			ani2++;
			lara.flare_frame = ani2;
		}
	}
	else
	{
		if ( lara_item->current_anim_state==AS_STOP && lara.skidoo==NO_ITEM)
		{
			lara_item->anim_number = STOP_A;
			lara_item->frame_number = STOP_F;
		}
/*
		if ( lara.left_arm.frame_base!=objects[FLARE].frame_base )
		{
			 lara.left_arm.frame_base=objects[FLARE].frame_base;
			 lara.flare_control_left = 1;
		}
*/
	}

	// If at holding flare frame, then go into throw
	if ( ani==FL_HOLD_F )
	{
		ani = FL_THROW_F;
	}
	// If igniting then go into throw straight after
	else if ( ani>=FL_IGNITE_F && ani<FL_IGNITE_F+23 )         // Lara is Igniting flare
	{
		ani++;
		if ( ani==FL_IGNITE_F+FL_IGNITE_FT-1)				   // If finished igniting, then throw it
			ani = FL_THROW_F;
	}
	// Do throw animation
	else if ( ani>=FL_THROW_F && ani<FL_DRAW_F ) 				// Lara is throwing flare
	{
		ani++;
		if ( ani==FL_THROWRELEASE_F )
		{
			// Gav: create a flare item and initialise it ready to fly through the air
			CreateFlare(1);
			undraw_flare_meshes();						// Swap meshes at appropriate frame
		}
		else if ( ani==FL_DRAW_F )
		{
			// Lara has finished throwing so end her flare nightmare
			ani = 0;
			lara.gun_type = lara.request_gun_type = lara.last_gun_type;
			lara.gun_status = LG_ARMLESS;
			InitialiseNewWeapon();
			lara.target = NULL;
			lara.left_arm.lock = lara.right_arm.lock = 0;
			lara.flare_control_left = 0;
			lara.flare_frame = 0;
		}
	}
	else if (ani>=FL_2HOLD_F && ani<FL_END_F)
	{
		ani++;
		if (ani==FL_END_F)
			ani = FL_THROW_F;
	}

	// Add dynamic light until flare leaves the hand in the throw
	if ( ani>=FL_THROW_F && ani<FL_THROWRELEASE_F )
		DoFlareInHand(lara.flare_age);

	lara.left_arm.frame_number = ani;
	set_flare_arm(lara.left_arm.frame_number);
}


/******************************************************************************
 *						Draw Flare Meshes
 *****************************************************************************/
void	draw_flare_meshes( void )
{
	lara.mesh_ptrs[HAND_L]  = meshes[objects[FLARE].mesh_index + HAND_L];
}
/******************************************************************************
 *						UnDraw ShotGun Meshes
 *****************************************************************************/
void	undraw_flare_meshes( void )
{
	lara.mesh_ptrs[HAND_L]  = meshes[objects[LARA].mesh_index + HAND_L];
}



/******************************************************************************
 *						Make Flare ready
 *****************************************************************************/
void	ready_flare( void )
{
	lara.gun_status = LG_ARMLESS; // drawn flare leaves us ARMLESS anyway
	lara.left_arm.x_rot = lara.left_arm.y_rot = lara.left_arm.z_rot = 0;
	lara.right_arm.x_rot = lara.right_arm.y_rot = lara.right_arm.z_rot = 0;
//	lara.left_arm.frame_number = lara.right_arm.frame_number = 0;
	lara.left_arm.lock = lara.right_arm.lock = 0;
	lara.target = NULL;
//	lara.left_arm.frame_base = lara.right_arm.frame_base = objects[FLARE].frame_base;
}


void FlareControl(sint16 item_number)
{
	/* Gav: make the flare move gracefully through the air (or lie still on the ground) */
	ITEM_INFO *flare;
	sint16 room_number;
	sint32 ceiling, height,h2;
	sint32 x, y, z, flare_age, xv, zv, bs,yang;
	FLOOR_INFO *floor;

	flare = &items[item_number];

	if (room[flare->room_number].flags & SWAMP)
	{
		KillItem(item_number); // Destroy flare when it hits a swamp room.
		return;
	}

	/* Spin flare whilst it is in the air */
	if (flare->fallspeed)
	{
		flare->pos.x_rot += (ONE_DEGREE*3);
		flare->pos.z_rot += (ONE_DEGREE*5);
	}
	else
	{
		flare->pos.x_rot = 0;
		flare->pos.z_rot = 0;
	}

	/* Remember last position */
	z = flare->pos.z_pos;
	y = flare->pos.y_pos;
	x = flare->pos.x_pos;

	flare->pos.z_pos += (zv = (flare->speed * phd_cos(flare->pos.y_rot) >> W2V_SHIFT));
	flare->pos.x_pos += (xv = (flare->speed * phd_sin(flare->pos.y_rot) >> W2V_SHIFT));

	/* Fall more slowly in water */
	if (room[flare->room_number].flags & UNDERWATER)
	{
		flare->fallspeed += (5 - flare->fallspeed)/2;
		flare->speed += (5 - flare->speed)/2;
	}
	else
		flare->fallspeed += GRAVITY;
	flare->pos.y_pos += flare->fallspeed;

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

	/* Bounce off floor */
	height = GetHeight(floor, flare->pos.x_pos, flare->pos.y_pos, flare->pos.z_pos);
	if (flare->pos.y_pos >= height)
	{
		flare->pos.y_rot &= 0xffff;	// Make angle unsigned.
		bs = 0;

		room_number = flare->room_number;
		floor = GetFloor(x,y,z, &room_number);
		h2 = GetHeight(floor, x,y,z);

		if ((height_type == BIG_SLOPE || height_type == DIAGONAL) && h2 < height)
		{
			yang = (long)((unsigned short) flare->pos.y_rot);
			if (tiltyoff < 0)
			{
				if (yang >= 0x8000)
					bs = 1;
			}
			else if (tiltyoff > 0)
			{
				if (yang <= 0x8000)
					bs = 1;
			}

			if (tiltxoff < 0)
			{
				if (yang >= 0x4000 && yang <= 0xc000)
					bs = 1;
			}
			else if (tiltxoff > 0)
			{
				if (yang <= 0x4000 || yang >= 0xc000)
					bs = 1;
			}
		}

		/* If last position of flare was also below this floor height, we've hit a wall, else we've hit a floor */

		if (y > height && bs == 0 &&
			(((x >> WALL_SHIFT) != (flare->pos.x_pos >> WALL_SHIFT)) ||
			 ((z >> WALL_SHIFT) != (flare->pos.z_pos >> WALL_SHIFT))))
		{
			// Need to know which direction the wall is.

			if ((x & (~(WALL_L-1))) != (flare->pos.x_pos & (~(WALL_L-1))))	// X crossed boundary?
			{
				if (xv <= 0)	// Hit angle = 0xc000.
					flare->pos.y_rot = 0x4000 + (0xc000 - flare->pos.y_rot);
				else			// Hit angle = 0x4000.
					flare->pos.y_rot = 0xc000 + (0x4000 - flare->pos.y_rot);
			}
			else		// Z crossed boundary.
			{
				flare->pos.y_rot = 0x8000 - flare->pos.y_rot;
			}

			flare->speed = flare->speed/2;

			/* Put flare back in its last position */
			flare->pos.x_pos = x;
			flare->pos.y_pos = y;
			flare->pos.z_pos = z;
			room_number = flare->room_number;
		}
		else if (height_type == BIG_SLOPE || height_type == DIAGONAL) 	// Hit a steep slope?
		{
			// Need to know which direction the slope is.

			flare->speed -= flare->speed>>2;

			if (tiltyoff < 0 && ((abs(tiltyoff)) - (abs(tiltxoff)) >= 2))	// Hit angle = 0x4000
			{
				if (((unsigned short)flare->pos.y_rot) > 0x8000)
				{
			 		flare->pos.y_rot = 0x4000 + (0xc000 - (unsigned short) flare->pos.y_rot - 1);
					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
				}
				else
				{
					if (flare->speed < 32)
					{
						flare->speed -= tiltyoff<<1;
						if ((unsigned short) flare->pos.y_rot > 0x4000 && (unsigned short) flare->pos.y_rot < 0xc000)
						{
							flare->pos.y_rot -= 4096;
							if ((unsigned short) flare->pos.y_rot < 0x4000)
								flare->pos.y_rot = 0x4000;
						}
						else if ((unsigned short) flare->pos.y_rot < 0x4000)
						{
							flare->pos.y_rot += 4096;
							if ((unsigned short) flare->pos.y_rot > 0x4000)
								flare->pos.y_rot = 0x4000;
						}
					}

					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
					else
						flare->fallspeed = 0;
				}
			}
			else if (tiltyoff > 0 && ((abs(tiltyoff)) - (abs(tiltxoff)) >= 2))	// Hit angle = 0xc000
			{
				if (((unsigned short) flare->pos.y_rot) < 0x8000)
				{
					flare->pos.y_rot = 0xc000 + (0x4000 - (unsigned short) flare->pos.y_rot - 1);
					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
				}
				else
				{
					if (flare->speed < 32)
					{
						flare->speed += tiltyoff<<1;
						if ((unsigned short) flare->pos.y_rot > 0xc000 || (unsigned short) flare->pos.y_rot < 0x4000)
						{
							flare->pos.y_rot -= 4096;
							if ((unsigned short) flare->pos.y_rot < 0xc000)
								flare->pos.y_rot = 0xc000;
						}
						else if ((unsigned short) flare->pos.y_rot < 0xc000)
						{
							flare->pos.y_rot += 4096;
							if ((unsigned short) flare->pos.y_rot > 0xc000)
								flare->pos.y_rot = 0xc000;
						}
					}

					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
					else
						flare->fallspeed = 0;
				}
			}
			else if (tiltxoff < 0 && ((abs(tiltxoff)) - (abs(tiltyoff)) >= 2))	// Hit angle = 0
			{
				if (((unsigned short) flare->pos.y_rot) > 0x4000 && ((unsigned short) flare->pos.y_rot) < 0xc000)
				{
					flare->pos.y_rot = (0x8000 - flare->pos.y_rot - 1);
					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
				}
				else
				{
					if (flare->speed < 32)
					{
						flare->speed -= tiltxoff<<1;

						if ((unsigned short) flare->pos.y_rot < 0x8000)
						{
							flare->pos.y_rot -= 4096;
							if ((unsigned short) flare->pos.y_rot > 0xf000)
								flare->pos.y_rot = 0;
						}
						else if ((unsigned short) flare->pos.y_rot >= 0x8000)
						{
							flare->pos.y_rot += 4096;
							if ((unsigned short) flare->pos.y_rot < 0x1000)
								flare->pos.y_rot = 0;
						}
					}

					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
					else
						flare->fallspeed = 0;
				}
			}
			else if (tiltxoff > 0 && ((abs(tiltxoff)) - (abs(tiltyoff)) >= 2))	// Hit angle = 0x8000
			{
				if (((unsigned short)flare->pos.y_rot) > 0xc000 || ((unsigned short)flare->pos.y_rot) < 0x4000)
				{
					flare->pos.y_rot = (0x8000 - flare->pos.y_rot - 1);
					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
				}
				else
				{
					if (flare->speed < 32)
					{
						flare->speed += tiltxoff<<1;

						if ((unsigned short) flare->pos.y_rot > 0x8000)
						{
							flare->pos.y_rot -= 4096;
							if ((unsigned short) flare->pos.y_rot < 0x8000)
								flare->pos.y_rot = 0x8000;
						}
						else if ((unsigned short) flare->pos.y_rot < 0x8000)
						{
							flare->pos.y_rot += 4096;
							if ((unsigned short) flare->pos.y_rot > 0x8000)
								flare->pos.y_rot = 0x8000;
						}
					}

					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
					else
						flare->fallspeed = 0;
				}
			}
			else if (tiltyoff < 0 && tiltxoff < 0)	// Hit angle = 0x2000
			{
				if (((unsigned short)flare->pos.y_rot) > 0x6000 && ((unsigned short)flare->pos.y_rot) < 0xe000)
				{
			 		flare->pos.y_rot = 0x2000 + (0xa000 - (unsigned short) flare->pos.y_rot - 1);
					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
				}
				else
				{
					if (flare->speed < 32)
					{
						flare->speed += (-tiltyoff) + (-tiltxoff);
						if ((unsigned short) flare->pos.y_rot > 0x2000 && (unsigned short) flare->pos.y_rot < 0xa000)
						{
							flare->pos.y_rot -= 4096;
							if ((unsigned short) flare->pos.y_rot < 0x2000)
								flare->pos.y_rot = 0x2000;
						}
						else if ((unsigned short) flare->pos.y_rot != 0x2000)
						{
							flare->pos.y_rot += 4096;
							if ((unsigned short) flare->pos.y_rot > 0x2000)
								flare->pos.y_rot = 0x2000;
						}
					}

					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
					else
						flare->fallspeed = 0;
				}
			}
			else if (tiltyoff < 0 && tiltxoff > 0)	// Hit angle = 0x6000
			{
				if (((unsigned short)flare->pos.y_rot) > 0xa000 || ((unsigned short)flare->pos.y_rot) < 0x2000)
				{
			 		flare->pos.y_rot = 0x6000 + (0xe000 - (unsigned short) flare->pos.y_rot - 1);
					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
				}
				else
				{
					if (flare->speed < 32)
					{
						flare->speed += (-tiltyoff) + tiltxoff;
						if ((unsigned short) flare->pos.y_rot < 0xe000 && (unsigned short) flare->pos.y_rot > 0x6000)
						{
							flare->pos.y_rot -= 4096;
							if ((unsigned short) flare->pos.y_rot < 0x6000)
								flare->pos.y_rot = 0x6000;
						}
						else if ((unsigned short) flare->pos.y_rot != 0x6000)
						{
							flare->pos.y_rot += 4096;
							if ((unsigned short) flare->pos.y_rot > 0x6000)
								flare->pos.y_rot = 0x6000;
						}
					}

					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
					else
						flare->fallspeed = 0;
				}
			}
			else if (tiltyoff > 0 && tiltxoff > 0)	// Hit angle = 0xa000
			{
				if (((unsigned short)flare->pos.y_rot) > 0xe000 || ((unsigned short)flare->pos.y_rot) < 0x6000)
				{
			 		flare->pos.y_rot = 0xa000 + (0x2000 - (unsigned short) flare->pos.y_rot - 1);
					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
				}
				else
				{
					if (flare->speed < 32)
					{
						flare->speed += tiltyoff + tiltxoff;
						if ((unsigned short) flare->pos.y_rot < 0x2000 || (unsigned short) flare->pos.y_rot > 0xa000)
						{
							flare->pos.y_rot -= 4096;
							if ((unsigned short) flare->pos.y_rot < 0xa000)
								flare->pos.y_rot = 0xa000;
						}
						else if ((unsigned short) flare->pos.y_rot != 0xa000)
						{
							flare->pos.y_rot += 4096;
							if ((unsigned short) flare->pos.y_rot > 0xa000)
								flare->pos.y_rot = 0xa000;
						}
					}

					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
					else
						flare->fallspeed = 0;
				}
			}
			else if (tiltyoff > 0 && tiltxoff < 0)	// Hit angle = 0xe000
			{
				if (((unsigned short)flare->pos.y_rot) > 0x2000 && ((unsigned short)flare->pos.y_rot) < 0xa000)
				{
			 		flare->pos.y_rot = 0xe000 + (0x6000 - (unsigned short) flare->pos.y_rot - 1);
					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
				}
				else
				{
					if (flare->speed < 32)
					{
						flare->speed += tiltyoff + (-tiltxoff);
						if ((unsigned short) flare->pos.y_rot < 0x6000 || (unsigned short) flare->pos.y_rot > 0xe000)
						{
							flare->pos.y_rot -= 4096;
							if ((unsigned short) flare->pos.y_rot < 0xe000)
								flare->pos.y_rot = 0xe000;
						}
						else if ((unsigned short) flare->pos.y_rot != 0xe000)
						{
							flare->pos.y_rot += 4096;
							if ((unsigned short) flare->pos.y_rot > 0xe000)
								flare->pos.y_rot = 0xe000;
						}
					}

					if (flare->fallspeed > 0)
						flare->fallspeed = -(flare->fallspeed>>1);
					else
						flare->fallspeed = 0;
				}
			}


			/* Put flare back in its last position */
			flare->pos.x_pos = x;
			flare->pos.y_pos = y;
			flare->pos.z_pos = z;
			room_number = flare->room_number;
		}
		else
		{
			/* Hit the floor; bounce and slow down */
			if (flare->fallspeed > 0)
			{
				if (flare->fallspeed > 16)
				{
					flare->fallspeed = -flare->fallspeed>>1;
					if (flare->fallspeed < -100)
						flare->fallspeed = -100;
				}
				else
				{
					/* Roll on floor */
					flare->fallspeed = 0;
					flare->speed -= 3;
					if (flare->speed < 0)
						flare->speed = 0;
				}
			}

			flare->pos.y_pos = height;
		}
	}
	else
	{
		/* Bounce off ceiling */
		ceiling = GetCeiling(floor, flare->pos.x_pos, flare->pos.y_pos, flare->pos.z_pos);
		if (flare->pos.y_pos < ceiling)
		{
			flare->fallspeed = -flare->fallspeed;
			flare->pos.y_pos = ceiling;
		}
	}

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

	/* Make flare older */
	flare_age = (sint32)flare->data & 0x7fff;

	if (flare_age < MAX_FLARE_AGE)
		flare_age++;
	else if (flare->fallspeed == 0 && flare->speed == 0)
	{
		KillItem(item_number); // destroy dead flare when it stops
		return;
	}

	if (DoFlareLight((PHD_VECTOR *)&flare->pos, flare_age))
	{
		flare_age |= 0x8000; // flag that flare needs a flame on the end

		if (room[flare->room_number].flags & UNDERWATER)
		{
			SoundEffect(12, &flare->pos, UNDERWATER);
			if (GetRandomDraw() < 0x4000)
				CreateBubble((PHD_3DPOS *)&flare->pos, flare->room_number);
		}
		else
			SoundEffect(12, &flare->pos, 0);
	}

	flare->data = (void *)flare_age;
}
