#ifdef PSX_VERSION
#include <sys\types.h>
#include <libgte.h>
#include <libgpu.h>
#include <gtemac.h>
#include <inline_c.h>
#include "../spec_psx/typedefs.h"
#include "../spec_psx/maths.h"
#else
#include "../specific/standard.h"
#include "../specific/global.h"
#endif

#include "objects.h"
#include "lara.h"
#include "laraanim.h"
#include "effect2.h"
#include "sphere.h"
#include "control.h"
#include "camera.h"
#include "traps.h"
#include "sound.h"

extern long wibble;
extern short rcossin_tbl[];

static uchar strut_fall;

void ControlArea51Rocket(sint16 item_number);
void Trigger51RocketSmoke(long x, long y, long z, long yv, long fire);
void Trigger51BlastFire(long x, long y, long z, long smoke, long end);
void ControlArea51Struts(sint16 item_number);
void InitialiseArea51Struts(sint16 item_number);

#if defined(PSX_VERSION) && defined(RELOC)

void *func[] = {
	&ControlArea51Rocket,  	// Main control for rocket && rocket smoke
	&Trigger51RocketSmoke,
	&Trigger51BlastFire,
	&ControlArea51Struts,  	// Main control for struts
	&InitialiseArea51Struts,
};

#endif

#define 	FIRE_ROOM	52
#define 	SMOKE_START	0
#define 	SMOKE_END	512
#define	MAX_ELEC_POS	256
#define	MAX_ELEC_POS2	MAX_ELEC_POS+(MAX_ELEC_POS>>1)

enum strut_anims {	STRUT_EMPTY, STRUT_WAIT, STRUT_FALL	};

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

	item = &items[item_number];

	if (!TriggerActive(item))
		return;

	if (item->object_number == SPECIAL_FX1)
	{
		if (item->required_anim_state < SMOKE_END)
		{
			item->required_anim_state+=8;
			if (item->required_anim_state >= SMOKE_END)
			{
				item->required_anim_state += 2048;
				item->goal_anim_state = 64;	// Setup initial blast radius.
				SoundEffect(106, NULL, 0);	// Initial blast.
			}
			else
			{
				SoundEffect(12, NULL, 0);	// Starting to erupt.
				item->goal_anim_state = item->current_anim_state = 0;
			}
		}
		else if (item->current_anim_state)	// Dying.
		{
			long	r, g, b, y;

			SoundEffect(257, NULL, 0);	// Erupting.
			item->required_anim_state += 32;
			if (item->required_anim_state > 16000)//16000)
			{
				KillItem(item_number);
				return;
			}

			r = 28+(GetRandomControl()&3);
			g = 12+(GetRandomControl()&7);
			b = GetRandomControl()&3;
			r = (r*(16384-item->required_anim_state))>>12;
			g = (g*(16384-item->required_anim_state))>>12;
			b = (b*(16384-item->required_anim_state))>>12;
			y = -(GetRandomControl()&511) + room[FIRE_ROOM].minfloor - 256;
			TriggerDynamic(item->pos.x_pos - 512 - 8192 + 1024,y,item->pos.z_pos-1024,24,r,g,b);
			camera.bounce = -((16384-item->required_anim_state)>>6);
			return;
		}
		else
		{
			if (!lara.burn)
			{
				long	x,rad;

				rad = (item->goal_anim_state < 8192) ? item->goal_anim_state : 8192;
				x = item->pos.x_pos - 512 - rad - 1024;
				if (lara_item->pos.x_pos > x)
				{
					lara_item->hit_points = 0;
					LaraBurn();
				}
		 		item->goal_anim_state+=80;
			}
			item->required_anim_state+=32;
		}

		if (item->required_anim_state < 4096+512)
		{
			if (item->goal_anim_state > 768)
				SoundEffect(257, NULL, 0);	// Erupting.
			if (item->goal_anim_state > 1024)
				item->goal_anim_state = 1024;
		}
		else
		{
			SoundEffect(257, NULL, 0);	// Erupting.
			if (item->required_anim_state > 12288)
				item->required_anim_state = 12288;

			if (item->goal_anim_state > 20480+2048)
			{
				long	dist,x,y,z,lp;

				for (lp=0;lp<64;lp++)
				{
					dist = (GetRandomControl() & 4095);
					x = item->pos.x_pos - 512 - 5120 - dist;
					z = item->pos.z_pos - 1024 + (GetRandomControl()&2047)-1024;
					y = -(GetRandomControl()&2047) + room[FIRE_ROOM].minfloor;
					Trigger51BlastFire(x,y,z,0,lp);
				}
				for (lp=64;lp<MAX_SPARKS>>1;lp++)
				{
					dist = (GetRandomControl() & 4095);
					x = item->pos.x_pos - 512 - 5120 - dist;
					z = item->pos.z_pos - 1024 + (GetRandomControl()&2047)-1024;
					y = -(GetRandomControl()&2047) + room[FIRE_ROOM].minfloor;
					Trigger51BlastFire(x,y,z,1,lp);
				}
				item->current_anim_state = 1;	// Set 'about to die flag'.
				camera.bounce = -((16384-item->required_anim_state)>>6);
				return;
			}

			item->fallspeed--;
			if (item->fallspeed < -1024)
				item->fallspeed = -1024;
			if (item->fallspeed < -72)
				strut_fall = 1;
			item->pos.y_pos += item->fallspeed>>2;
			room_number = item->room_number;
			floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
			if (room_number != item->room_number)
				ItemNewRoom(item_number, room_number);
		}

		if (item->required_anim_state < 8192 && item->required_anim_state > 64)
			camera.bounce = -(item->required_anim_state>>6);
		else	if (item->required_anim_state >= 8192)
			camera.bounce = -((16384-item->required_anim_state)>>6);
		else
			camera.bounce=-1;
	}

	if ((wibble&12)==0)
	{
		long yvel,f;

		if (item->required_anim_state < (SMOKE_END>>2))
			yvel = GetRandomControl()&31;
		else
			yvel = (item->required_anim_state-(SMOKE_END>>2) + (GetRandomControl()&31)) << 2;

		if (yvel > (192<<5))
			yvel = 192<<5;

		f = (item->required_anim_state < SMOKE_END) ? 0:1;

		Trigger51RocketSmoke(item->pos.x_pos-512-256-128, item->pos.y_pos-64, item->pos.z_pos-512, yvel,f);
		Trigger51RocketSmoke(item->pos.x_pos-512+256+128, item->pos.y_pos-64, item->pos.z_pos-512, yvel,f);
		Trigger51RocketSmoke(item->pos.x_pos-512, item->pos.y_pos-64, item->pos.z_pos-512-256-128, yvel,f);
		Trigger51RocketSmoke(item->pos.x_pos-512, item->pos.y_pos-64, item->pos.z_pos-512+256+128, yvel,f);
	}


	if (item->goal_anim_state)
	{
		long	dist,x,y,z,yand,rad;

		TriggerDynamic(item->pos.x_pos - 512 ,item->pos.y_pos,item->pos.z_pos-512,31,28+(GetRandomControl()&3),12+(GetRandomControl()&7), GetRandomControl()&3);

		rad = (item->goal_anim_state < 8192) ? item->goal_anim_state : 8192;
		dist = GetRandomControl() & 2047;
		x = item->pos.x_pos - 512 - rad - dist + 1024;
		z = item->pos.z_pos-1024 + (GetRandomControl()&2047)-1024;

		if (item->goal_anim_state < 1024)
			yand = 255;
		else
			yand = 2047;

		y = -(GetRandomControl()&yand) + room[FIRE_ROOM].minfloor;
		if (wibble&4)
			Trigger51BlastFire(x,y,z,0,-1);

		TriggerDynamic(item->pos.x_pos - 512 - rad + 1024,y,item->pos.z_pos-1024,24,28+(GetRandomControl()&3),12+(GetRandomControl()&7), GetRandomControl()&3);

		if (wibble&4)
		{
			dist = GetRandomControl() & 1023;
			x = item->pos.x_pos - 512 - rad - dist;
			z = item->pos.z_pos-1024 + (GetRandomControl()&2047)-1024;

			if (item->goal_anim_state < 1024)
				yand = 255;
			else
				yand = 2047;
			y = -(GetRandomControl()&yand) + room[FIRE_ROOM].minfloor;
			Trigger51BlastFire(x,y,z,1,-1);
		}
	}
}

void Trigger51RocketSmoke(long x, long y, long z, long yv, long fire)
{
	long		size;
	SPARKS	*sptr;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	if (fire)
	{
		sptr->sR = 48+(GetRandomControl()&31);
		sptr->sG = sptr->sR;
		sptr->sB = 192+(GetRandomControl()&63);

		sptr->dR = 192+(GetRandomControl()&63);
		sptr->dG = 128+(GetRandomControl()&63);
		sptr->dB = 32;
	}
	else
	{
		sptr->sR = 0;
		sptr->sG = 0;
		sptr->sB = 0;
		sptr->dR = 32+(yv>>5);
		sptr->dG = 32+(yv>>5);
		sptr->dB = 32+(yv>>5);
	}

	sptr->ColFadeSpeed = 16 - ((fire) ? (yv>>9) : 0);
	sptr->FadeToBlack = (fire) ? 0 : 16;
	sptr->sLife = sptr->Life = (GetRandomControl()&3) + 60 - ((fire) ? (yv>>8) : 0);

	sptr->TransType = COLADD;

	sptr->extras = 0;
	sptr->Dynamic = -1;

	sptr->x = x + ((GetRandomControl()&15)-8);
	sptr->y = y;
	sptr->z = z + ((GetRandomControl()&15)-8);
	sptr->Xvel = ((GetRandomControl()&255)-128);
	sptr->Yvel = yv+(GetRandomControl()&15);
	sptr->Zvel = ((GetRandomControl()&255)-128);
	sptr->Friction = 4;

	if (GetRandomControl()&1)
	{
		sptr->Flags = SP_SCALE|SP_DEF|SP_ROTATE|SP_EXPDEF;
		sptr->RotAng = GetRandomControl()&4095;
		if (GetRandomControl()&1)
			sptr->RotAdd = -(GetRandomControl()&15)-16;
		else
			sptr->RotAdd = (GetRandomControl()&15)+16;
	}
	else
		sptr->Flags = SP_SCALE|SP_DEF|SP_EXPDEF;

	sptr->Def = objects[EXPLOSION1].mesh_index;
	sptr->Scalar = 3;
	sptr->Gravity = sptr->MaxYvel = 0;
	size = (GetRandomControl()&63)+64+(yv>>5);
	if (size>255)
		size = 255;
	sptr->Width = sptr->sWidth = size>>2;
	sptr->dWidth = size;
 	sptr->Height = sptr->sHeight = size>>2;
	sptr->dHeight = size;
}


void Trigger51BlastFire(long x, long y, long z, long smoke, long end)
{
	long		size;
	SPARKS	*sptr;

	if (end>=0)
		sptr = &spark[end];
	else
		sptr = &spark[GetFreeSpark()];

	sptr->On = 1;

/*	sptr->sR = 0;
	sptr->sG = 64+(GetRandomControl()&31);
	sptr->sB = 0;
	sptr->dR = 0;
	sptr->dG = 160+(GetRandomControl()&31);
	sptr->dB = 0;*/

	if (smoke)
	{
		sptr->sR = 0;
		sptr->sG = 0;
		sptr->sB = 0;
		sptr->dR = 64;
		sptr->dG = 64;
		sptr->dB = 64;
	}
	else
	{
		sptr->sR = 128+(GetRandomControl()&31);
		sptr->sG = 64+(GetRandomControl()&31);
		sptr->sB = 32;
		sptr->dR = 224+(GetRandomControl()&31);
		sptr->dG = 160+(GetRandomControl()&31);
		sptr->dB = 32;
	}

	sptr->ColFadeSpeed = 16;
	if (end)
	{
		sptr->FadeToBlack = 32+(GetRandomControl()&31);
		sptr->sLife = sptr->Life = 72 + (end>>1);
	}
	else
	{
		sptr->FadeToBlack = 8+((smoke)?24:0);
		sptr->sLife = sptr->Life = (GetRandomControl()&7) + 32 + ((smoke)?32:0);
	}

	sptr->TransType = COLADD;

	sptr->extras = 0;
	sptr->Dynamic = -1;

	sptr->x = x + ((GetRandomControl()&15)-8);
	sptr->y = y;
	sptr->z = z + ((GetRandomControl()&15)-8);
	sptr->Xvel = ((GetRandomControl()&255)-128);
	sptr->Yvel = -(GetRandomControl()&7);
	sptr->Zvel = ((GetRandomControl()&255)-128);
	sptr->Friction = 4;

	sptr->Flags = SP_SCALE|SP_DEF|SP_ROTATE|SP_EXPDEF;
	sptr->RotAng = GetRandomControl()&4095;
	if (GetRandomControl()&1)
		sptr->RotAdd = -(GetRandomControl()&15)-16;
	else
		sptr->RotAdd = (GetRandomControl()&15)+16;

	sptr->Def = objects[EXPLOSION1].mesh_index;
	sptr->Scalar = 4;
	sptr->Gravity = sptr->MaxYvel = 0;
	size = (GetRandomControl()&63)+64;
	if (end)
		sptr->Width = sptr->sWidth = sptr->dWidth = sptr->Height = sptr->sHeight = sptr->dHeight = size;
	else
	{
		sptr->Width = sptr->sWidth = size>>1;
		sptr->dWidth = size;
 		sptr->Height = sptr->sHeight = size>>1;
		sptr->dHeight = size;
	}
}

void InitialiseArea51Struts(sint16 item_number)
{
	strut_fall = 0;
}

void ControlArea51Struts(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	if (!strut_fall)
		item->current_anim_state = STRUT_WAIT;
	else if (item->goal_anim_state != STRUT_FALL && item->current_anim_state != STRUT_FALL)
		item->goal_anim_state = STRUT_FALL;

	AnimateItem(item);
}
