/***************************************************************************************************************/
/*                                                                                                             */
/* Missle and Body Part and Explosions etc                                                      G.Rummery 1997 */
/*                                                                                                             */
/***************************************************************************************************************/

// see 'box.h' for header info

#include "game.h"

#define KNIFE_DAMAGE 50
#define DIVER_HARPOON_DAMAGE 50

#define MISSILE_RANGE SQUARE(WALL_L/6)
#define ROCKET_RANGE SQUARE(WALL_L)

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

void ControlMissile(sint16 fx_number)
{
	FX_INFO *fx;
	FLOOR_INFO *floor;
	sint16 room_number;
	sint32 speed;

	fx = &effects[fx_number];

	if (fx->object_number == DIVER_HARPOON && !(room[fx->room_number].flags & UNDERWATER) && fx->pos.x_rot > -0x3000)
		fx->pos.x_rot -= ONE_DEGREE;

	fx->pos.y_pos += (fx->speed * phd_sin(-fx->pos.x_rot) >> W2V_SHIFT);
	speed = fx->speed * phd_cos(fx->pos.x_rot) >> W2V_SHIFT;
	fx->pos.z_pos += (speed * phd_cos(fx->pos.y_rot) >> W2V_SHIFT);
	fx->pos.x_pos += (speed * phd_sin(fx->pos.y_rot) >> W2V_SHIFT);
	room_number = fx->room_number;
	floor = GetFloor(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, &room_number);

	/* Check for hitting something */
	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))
	{
		if (fx->object_number == KNIFE || fx->object_number == DIVER_HARPOON)
		{
			/* Change shard into ricochet */
//			fx->speed = 0;
//			fx->frame_number = -GetRandomControl()/11000;
//			fx->counter = 6;
//			fx->object_number = RICOCHET1;
			SoundEffect((fx->object_number == DIVER_HARPOON)? 10 : 258, &fx->pos, 0);
		}
		else if (fx->object_number == DRAGON_FIRE)
		{
			AddDynamicLight(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, 14, 11);
			KillEffect(fx_number);
		}
		return;
	}

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

	/* Check for hitting Lara */
	if (fx->object_number == DRAGON_FIRE)
	{
		if (ItemNearLara(&fx->pos, 350))
		{
			lara_item->hit_points -= 3;
			lara_item->hit_status = 1;
			LaraBurn();
			return;
		}
	}
	else if (ItemNearLara(&fx->pos, 200))
	{
		if (fx->object_number == KNIFE)
		{
			lara_item->hit_points -= KNIFE_DAMAGE;
			SoundEffect(317, &fx->pos, 0);
			KillEffect(fx_number);
		}
		else if (fx->object_number == DIVER_HARPOON)
		{
			lara_item->hit_points -= DIVER_HARPOON_DAMAGE;
			SoundEffect(317, &fx->pos, 0);
			KillEffect(fx_number);
		}
		lara_item->hit_status = 1;

		fx->pos.y_rot = lara_item->pos.y_rot;
		fx->speed = lara_item->speed;
		fx->frame_number = fx->counter = 0;
	}

	/* Create bubbles in wake of harpoon bolt */
	if (fx->object_number == DIVER_HARPOON && room[fx->room_number].flags & UNDERWATER)
		CreateBubble(&fx->pos, fx->room_number);
	else if (fx->object_number == DRAGON_FIRE && !fx->counter--)
	{
		AddDynamicLight(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, 14, 11);
		SoundEffect(305, &fx->pos, 0);
		KillEffect(fx_number);
	}
	else if (fx->object_number == KNIFE)
		fx->pos.z_rot += 30*ONE_DEGREE;
}


void ShootAtLara(FX_INFO *fx)
{
	sint32 x, y, z, distance;
	sint16 *bounds;

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

	bounds = GetBoundsAccurate(lara_item);
	y += bounds[3] + (bounds[2] - bounds[3]) * 3/4;

	distance = phd_sqrt(SQUARE(x) + SQUARE(z));
	fx->pos.x_rot = -phd_atan(distance, y);
	fx->pos.y_rot = phd_atan(z, x);

	/* Random scatter (only a little bit else it's too hard to avoid) */
	fx->pos.x_rot += (GetRandomControl() - 0x4000)/0x40;
	fx->pos.y_rot += (GetRandomControl() - 0x4000)/0x40;
}


#ifdef OLD_TOMB_REF
sint16 ShardGun(sint32 x, sint32 y, sint32 z, sint16 speed, PHD_ANGLE yrot, sint16 room_number)
{
	// MISSILE2
	sint16 fx_number;
	FX_INFO *fx;

	fx_number = CreateEffect(room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->pos.x_pos = x;
		fx->pos.y_pos = y;
		fx->pos.z_pos = z;
		fx->room_number = room_number;
		fx->pos.x_rot = fx->pos.z_rot = 0;
		fx->pos.y_rot = yrot;
		fx->speed = SHARD_SPEED;
		fx->frame_number = 0;
		fx->object_number = MISSILE2;
		fx->shade = 14 * 256;
		ShootAtLara(fx);
	}

	return (fx_number);
}


sint16 RocketGun(sint32 x, sint32 y, sint32 z, sint16 speed, PHD_ANGLE yrot, sint16 room_number)
{
	// MISSILE3
	sint16 fx_number;
	FX_INFO *fx;

	fx_number = CreateEffect(room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->pos.x_pos = x;
		fx->pos.y_pos = y;
		fx->pos.z_pos = z;
		fx->room_number = room_number;
		fx->pos.x_rot = fx->pos.z_rot = 0;
		fx->pos.y_rot = yrot;
		fx->speed = ROCKET_SPEED;
		fx->frame_number = 0;
		fx->object_number = MISSILE3;
		fx->shade = 16 * 256;
		ShootAtLara(fx);
	}

	return (fx_number);
}
#endif


/***************************************************************************************
 *	 Go through heirarchy getting positions of mesh parts and spawning effect meshes
 *	'mesh_bits' flags which parts to do this to (allows bits to be blown off things)
 *		returns true if Item completely destroyed!!
 **************************************************************************************/
int ExplodingDeath(sint16 item_number, sint32 mesh_bits, sint16 damage)
{
	int i, poppush;
	ITEM_INFO *item;
	OBJECT_INFO *object;
	sint32 *bone, bit;
	sint16 *rotation;
	sint16 *frame, *extra_rotation;
	sint16 fx_number;
	FX_INFO *fx;

	item = &items[item_number];
	object = &objects[item->object_number];

	*(phd_mxptr+M23) = 0; // dodgy frig (no depthQ please)
	S_CalculateLight(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, item->room_number, &item->il);

	frame = GetBestFrame(item);

	phd_PushUnitMatrix();
	*(phd_mxptr+M03) = 0;
	*(phd_mxptr+M13) = 0;
	*(phd_mxptr+M23) = 0;
	phd_RotYXZ(item->pos.y_rot, item->pos.x_rot, item->pos.z_rot);

	/* Translate object 0 */
	phd_TranslateRel((sint32)*(frame+6), (sint32)*(frame+7), (sint32)*(frame+8));

	/* Switch to sint32 pointer for packed_rotation values */
	rotation = frame+9;
	gar_RotYXZsuperpack(&rotation, 0);

	bone = bones + object->bone_index;
	extra_rotation = (sint16 *)item->data;

	bit = 1;

	if ((bit & mesh_bits) && (bit & item->mesh_bits))
	{
		if ((GetRandomControl()&3)==0)
		{
			fx_number = CreateEffect(item->room_number);
			if (fx_number != NO_ITEM)
			{
				fx = &effects[fx_number];
				fx->pos.x_pos = (*(phd_mxptr+M03)>>W2V_SHIFT) + item->pos.x_pos;
				fx->pos.y_pos = (*(phd_mxptr+M13)>>W2V_SHIFT) + item->pos.y_pos;
				fx->pos.z_pos = (*(phd_mxptr+M23)>>W2V_SHIFT) + item->pos.z_pos;
				fx->room_number = item->room_number;
				fx->pos.y_rot = (GetRandomControl() - 0x4000)<<1;
				fx->speed =(sint16) GetRandomControl() >> 8;
				fx->fallspeed =(sint16) -GetRandomControl() >> 8;
				fx->counter = damage;
				fx->frame_number = object->mesh_index;
				fx->object_number = BODY_PART;
				fx->shade = ls_adder - 0x300;
			}
			item->mesh_bits -= bit;
		}
	}

	for (i=1; i<object->nmeshes; i++, bone+=3)
	{
		poppush = *(bone++);			// Do Automated Hierarchy
		if (poppush & 1)                // Push Pop
			phd_PopMatrix();
		if (poppush & 2)
			phd_PushMatrix();

		phd_TranslateRel(*(bone), *(bone+1), *(bone+2));
		gar_RotYXZsuperpack(&rotation, 0);

		/* Bone structure records if extra rotations are required by control routines */
		if (poppush & (ROT_X|ROT_Y|ROT_Z))
		{
			if (poppush & ROT_Y)
				phd_RotY(*(extra_rotation++));
			if (poppush & ROT_X)
				phd_RotX(*(extra_rotation++));
			if (poppush & ROT_Z)
				phd_RotZ(*(extra_rotation++));
		}

		bit <<= 1;
		if ((bit & mesh_bits) && (bit & item->mesh_bits))
		{
			if ((GetRandomControl()&3)==0)
			{
				fx_number = CreateEffect(item->room_number);
				if (fx_number != NO_ITEM)
				{
					fx = &effects[fx_number];
					fx->pos.x_pos = (*(phd_mxptr+M03)>>W2V_SHIFT) + item->pos.x_pos;
					fx->pos.y_pos = (*(phd_mxptr+M13)>>W2V_SHIFT) + item->pos.y_pos;
					fx->pos.z_pos = (*(phd_mxptr+M23)>>W2V_SHIFT) + item->pos.z_pos;
					fx->room_number = item->room_number;
					fx->pos.y_rot = (GetRandomControl() - 0x4000)<<1;
					fx->speed =(sint16) GetRandomControl() >> 8;
					fx->fallspeed =(sint16) -GetRandomControl() >> 8;
					fx->counter = damage;
					fx->frame_number = object->mesh_index + i;
					fx->object_number = BODY_PART;
					fx->shade = ls_adder - 0x300;
				}
				item->mesh_bits -= bit;
			}
		}
	}

	phd_PopMatrix();

	/* Might have been blown to bits! */
	if (!((0x7fffffff >> (31 - object->nmeshes)) & item->mesh_bits))
		return(1);
	else
		return(0);
}


void ControlBodyPart(sint16 fx_number)
{
	FX_INFO *fx, *splash;
	FLOOR_INFO *floor;
	sint32 height, ceiling, lp;
	sint16 room_number, new_fx;

	fx = &effects[fx_number];

	fx->pos.x_rot += (ONE_DEGREE*5);
	fx->pos.z_rot += (ONE_DEGREE*10);
	fx->pos.z_pos += (fx->speed * phd_cos(fx->pos.y_rot) >> W2V_SHIFT)>>2;
	fx->pos.x_pos += (fx->speed * phd_sin(fx->pos.y_rot) >> W2V_SHIFT)>>2;
	fx->fallspeed += GRAVITY>>1;
	fx->pos.y_pos += fx->fallspeed;

	if ((wibble & 12) == 0)
	{
		if (fx_number&1)
			TriggerFireFlame(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, fx_number, 0);
		TriggerFireSmoke(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, -1, 0);
	}

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

	/* Splosh if passed into water */
/*	if (!(room[fx->room_number].flags&UNDERWATER) && (room[room_number].flags&UNDERWATER))
	{
		//SoundEffect(33, &fx->pos, 0);
		new_fx = CreateEffect(fx->room_number);
		if (new_fx != NO_ITEM)
		{
			splash = &effects[new_fx];
			splash->pos.x_pos = fx->pos.x_pos;
			splash->pos.y_pos = fx->pos.y_pos;
			splash->pos.z_pos = fx->pos.z_pos;
			splash->pos.y_rot = 0;
			splash->speed = 0;
			splash->frame_number = 0;
			splash->object_number = SPLASH1;
		}
	}     */

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

	/* Absorbed by floor */
	height = GetHeight(floor, fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos);
	if (fx->pos.y_pos >= height)
	{
		/* Change into explosion */
		if (fx->counter)
		{
//			fx->speed = 0;
//			fx->frame_number = 0;
//			fx->counter = 0;
//			fx->object_number = EXPLOSION1;
			for (lp=0;lp<3;lp++)
			{
				TriggerFireFlame(fx->pos.x_pos, height, fx->pos.z_pos,-1, 0);
				TriggerFireSmoke(fx->pos.x_pos, height, fx->pos.z_pos,-1, 0);
			}
			SoundEffect(105, &fx->pos, 0);
			KillEffect(fx_number);
		}
		else
		{
			for (lp=0;lp<3;lp++)
			{
				TriggerFireFlame(fx->pos.x_pos, height, fx->pos.z_pos,-1, 0);
				TriggerFireSmoke(fx->pos.x_pos, height, fx->pos.z_pos,-1, 0);
			}
			KillEffect(fx_number);
		}
		return;
	}

	/* Hit Lara and do her some damage the saucy trollop; fx->counter is damage (plus doubles as damage radius) */
	if (ItemNearLara(&fx->pos, fx->counter*2))
	{
		lara_item->hit_points -= fx->counter;
		lara_item->hit_status = 1;

		/* Change into explosion */
		if (fx->counter)
		{
			fx->speed = 0;
			fx->frame_number = 0;
			fx->counter = 0;
			fx->object_number = EXPLOSION1;
			SoundEffect(105, &fx->pos, 0);

			lara.spaz_effect_count = 5;
			lara.spaz_effect = fx;
		}
		else
			KillEffect(fx_number);
	}

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