#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,(rand()&6)+8,(rand()&7)+24,(rand()&7)+8,rand()&15);
			return (0);
		}
	}
	else
	{
		TriggerDynamic(x,y,z,16-((flare_age-FLARE_DEAD)>>1),(rand()&7)+24,(rand()&7)+8,rand()&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);

			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((rand()&127)-64,(rand()&127)-64,(rand()&511)+512);
			for (lp=0;lp<(rand()&3)+4;lp++)
				TriggerFlareSparks(xv,yv,zv, (phd_mxptr[M03]>>W2V_SHIFT),(phd_mxptr[M13]>>W2V_SHIFT),(phd_mxptr[M23]>>W2V_SHIFT));
			phd_PopMatrix();
#endif
		}
	}
	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;
	sint32 x, y, z, flare_age;
	FLOOR_INFO *floor;

	flare = &items[item_number];

	/* 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 += flare->speed * phd_cos(flare->pos.y_rot) >> W2V_SHIFT;
	flare->pos.x_pos += 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)
	{
		/* 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)
		{
			/* Need to know which direction the wall is; ho hum */
			flare->pos.y_rot += -0x8000;
			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
		{
			/* Hit the floor; bounce and slow down */
			if (flare->fallspeed > 40)
			{
				flare->fallspeed = -flare->fallspeed + 40;
				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;
		}
	}

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