#include <sys\types.h>
#include <libgte.h>
#include <libgpu.h>
#include <gtemac.h>
#include <inline_c.h>
#include "maths.h"
#include "include.h"

#define	X	0
#define	Y	1
#define	Z	2
#define	XYZ	3

extern	SVECTOR3D CamRot;
extern	DB_STRUCT db;						// The double buffering info
extern	unsigned short sincos_tbl[];
extern	long	wibble;
extern	sint32 phd_persp;			// Perspective Scale Factor

#define	SP_FLAT		1
#define	SP_SCALE		2
#define	SP_RICOCHET	4
#define	SP_DEF		8
#define	SP_ROTATE		16
#define	SP_EXPLOSION	32

#define	SD_EXPLOSION	1

#define	SEMITRANS	1
#define	COLADD	2
#define	COLSUB	3
#define	WEIRD	4

#define	MAX_SPARKS	128

static	long	next_spark = 0;
long	GetFreeSpark();
void TriggerAlertLight(long x, long y, long z, long r, long g, long b, long angle, long room_no);

typedef struct
{
	long		x,y,z;

	short	Xvel;
	short	Yvel;
	short	Zvel;
	short	Gravity;		// Y vel.

	short	RotAng;
	uchar	Friction; 	// X & Z vels - (vels -= vels>>Friction)	// Top nibble = Yvel friction.
	uchar	Flags;		// 1 = flat, 2 = scale size, 4 = ricochet spark

	uchar	sWidth;
	uchar	dWidth;
	uchar	sHeight;
	uchar	dHeight;

	uchar	Width;
	uchar	Height;
	uchar	Scalar;
	uchar	Def;

	signed 	char	RotAdd;
	signed	char	MaxYvel;
	uchar	pad[1];

// Size = 36

} SPARKLET;

typedef struct
{
	uchar	On;		// Used ?
	uchar	sR;		//
	uchar	sG;  	// Start RGB.
	uchar	sB;  	//

	uchar	dR;		//
	uchar	dG;  	// End RGB.
	uchar	dB;  	//
	uchar	R;		//

	uchar	G;  	// Current RGB.
	uchar	B;  	//
	uchar	ColFadeSpeed;	// Speed at which to fade to endcol in frames.
	uchar	FadeToBlack;	// When to start fading to black (life left). (0 = never).

	uchar	sLife;
	uchar 	Life;	// Start life, current life.
	uchar	TransType;	// 0 = Semi, 1 = Col add, 2 = Colsub, 3 = weird!

	uchar	extras;
	signed	char	Dynamic;	// Which dynamic-light-controller to use. -1 = no dynamic.
	uchar	pad[3];

	SPARKLET	Sps;//[MAX_SPARKLETS];   // Slave sparks.

// Size = 20 + 36 = 56

} SPARKS;

typedef struct
{
	uchar	On;
	uchar	Falloff;	// 0 - 31. (1/4 block per value).
	uchar 	R;
	uchar 	G;
	uchar 	B;
	uchar	Flags;
	uchar	Pad[3];
} SP_DYNAMIC;

SP_DYNAMIC	spark_dynamics[32];
SPARKS		spark[MAX_SPARKS];

void UpdateSparks()
{
	long		lp,lp1;
	long		divmult;

	long		random;

	SPARKS	*sptr;
	SPARKLET	*slptr;

	sptr = &spark[0];

	for (lp=0;lp<MAX_SPARKS;lp++)
	{
		if (sptr->On == 0)
		{
			sptr++;
			continue;
		}

		sptr->Life--;
		if (sptr->Life == 0)
		{
			if (sptr->Dynamic != -1)
				spark_dynamics[sptr->Dynamic].On = 0;	// Turn off dynamic light controller.
			sptr->On=0;
			sptr++;
			continue;
		}

		if ((sptr->sLife - sptr->Life) < sptr->ColFadeSpeed)	// Faded to colour already?
		{
			divmult = (65536*(sptr->sLife-sptr->Life)) / sptr->ColFadeSpeed;
			sptr->R = sptr->sR + (((sptr->dR - sptr->sR) * divmult)>>16);
			sptr->G = sptr->sG + (((sptr->dG - sptr->sG) * divmult)>>16);
			sptr->B = sptr->sB + (((sptr->dB - sptr->sB) * divmult)>>16);
		}
		else	if (sptr->Life < sptr->FadeToBlack)	// Fade to black yet?
		{
			divmult = 65536-((65536*(sptr->FadeToBlack-sptr->Life)) / sptr->FadeToBlack);
			sptr->R = (sptr->dR * divmult)>>16;
			sptr->G = (sptr->dG * divmult)>>16;
			sptr->B = (sptr->dB * divmult)>>16;
		}
		else
		{
			sptr->R = sptr->dR;
			sptr->G = sptr->dG;
			sptr->B = sptr->dB;
		}

		if (((sptr->sLife - sptr->Life) == (sptr->extras>>3)) && (sptr->extras&7))	// Ready to trigger more?
		{
			for (lp1=0;lp1<(sptr->extras&7);lp1++)
			{
	  			TriggerExplosionSparks(sptr->Sps.x,sptr->Sps.y,sptr->Sps.z,(sptr->extras&7)-1,sptr->Dynamic);
				sptr->Dynamic = -1;
				VIBRATE(0xc00, 0xc00-((sptr->extras&7)*0x300), 20, 0x40, 60);
			}
			sptr->extras = 0;
		}

		divmult = (65536*(sptr->sLife-sptr->Life)) / sptr->sLife;

		slptr = &sptr->Sps;
//		for (lp1=0;lp1<sptr->NumSps;lp1++)
		{
			slptr->Yvel+=slptr->Gravity;
			if (slptr->MaxYvel)
			{
				if ((slptr->Yvel < 0 && slptr->Yvel < (slptr->MaxYvel<<5)) ||
					(slptr->Yvel > 0 && slptr->Yvel > (slptr->MaxYvel<<5)))
					slptr->Yvel = slptr->MaxYvel<<5;
			}

			if (slptr->Friction&15)
			{
				slptr->Xvel-=(slptr->Xvel>>(slptr->Friction&15));
				slptr->Zvel-=(slptr->Zvel>>(slptr->Friction&15));
			}

			if ((slptr->Friction>>4)&15)
				slptr->Yvel-=(slptr->Yvel>>((slptr->Friction>>4)&15));

			slptr->x += slptr->Xvel>>5;
			slptr->y += slptr->Yvel>>5;
			slptr->z += slptr->Zvel>>5;

			slptr->Width = slptr->sWidth + (((slptr->dWidth - slptr->sWidth) * divmult)>>16);
			slptr->Height = slptr->sHeight + (((slptr->dHeight - slptr->sHeight) * divmult)>>16);
//			slptr++;
		}
		sptr++;
	}

// Update and trigger any dynamic lights associated with sparks.

	sptr = &spark[0];
	for (lp=0;lp<MAX_SPARKS;lp++)
	{
		if (sptr->On && sptr->Dynamic != -1)
		{
			long		r,g,b,falloff;
			long		x,y,z;
			SP_DYNAMIC	*sdptr;

			sdptr = &spark_dynamics[sptr->Dynamic];
			if (sdptr->Flags & SD_EXPLOSION)
			{
				random = GetRandomDraw();

				x = sptr->Sps.x + ((random & 0xf)<<4);
				y = sptr->Sps.y + ((random & 0xf0) );
				z = sptr->Sps.z + ((random & 0xf00) >> 4);

				falloff = sptr->sLife - sptr->Life - 1;
				if (falloff < 2)
				{
					if (sdptr->Falloff < 28)
						sdptr->Falloff+=6;
					r = 31-(random&3)-(falloff);
					g = 31-(random&3)-(falloff<<1);
					b = 31-(random&3)-(falloff<<3);
				}
				else if (falloff < 4)
				{
					if (sdptr->Falloff < 28)
						sdptr->Falloff+=6;
					r = 31-(random&3)-(falloff);
					g = 16-(falloff);
					b = 16-(falloff<<2);
					if (b<0)
						b=0;
				}
				else
				{
					if (sdptr->Falloff > 0)
						sdptr->Falloff--;
					r = 28+(random&3);
					g = 16+((random>>4)&3);
					b = ((random>>8)&7);
				}
				TriggerDynamic(x,y,z,((sdptr->Falloff > 31) ? 31:sdptr->Falloff),r,g,b);
			}

		}
		sptr++;
	}
}

void DrawSparks()
{
	long		lp,lp1;
	long		result[XYZ];
	long		scr[3][XYZ];
	long		w,h,x1,x2,x3,x4,y1,y2,y3,y4;

	SPARKS	*sptr;
	SPARKLET	*slptr;

	POLY_F3	*polyf3;
	TILE 	*polytile;

	POLY_FT4 	*polyft4;
	PSXSPRITESTRUCT* pSpriteInfo;

	SetRotMatrix(Matrix);
	SetTransMatrix(Matrix);

	sptr = &spark[0];

	for (lp=0;lp<MAX_SPARKS;lp++)
	{
		if (sptr->On == 0)
		{
			sptr++;
			continue;
		}

		slptr = &sptr->Sps;
//		for (lp1=0;lp1<sptr->NumSps;lp1++)
		{
			mCalcPoint(slptr->x,slptr->y,slptr->z,&result[0]);
			ProjectPCoord(result[X],result[Y],result[Z],&scr[0][0],192,120,phd_persp);

			if (slptr->Flags & SP_RICOCHET)
			{
				polyf3 = (POLY_F3 *) db.polyptr;

				mCalcPoint(slptr->x-(slptr->Xvel>>3),slptr->y-(slptr->Yvel>>3),slptr->z-(slptr->Zvel>>3),&result[0]);
				ProjectPCoord(result[X],result[Y],result[Z],&scr[1][0],192,120,phd_persp);
				mCalcPoint(slptr->x-(slptr->Xvel>>5),slptr->y-(slptr->Yvel>>5)+slptr->Height,slptr->z-(slptr->Zvel>>5),&result[0]);
				ProjectPCoord(result[X],result[Y],result[Z],&scr[2][0],192,120,phd_persp);

				if ( (scr[0][Z] < 32 || scr[0][Z] > 16384) ||
					(scr[1][Z] < 32 || scr[1][Z] > 16384) ||
					(scr[2][Z] < 32 || scr[2][Z] > 16384) ||
					(scr[0][X] < 0  && scr[1][X] < 0 && scr[2][X] < 0) ||
					(scr[0][X] > 383  && scr[1][X] > 383 && scr[2][X] > 383) ||
					(scr[0][Y] < 0  && scr[1][Y] < 0 && scr[2][Y] < 0) ||
					(scr[0][Y] > 239  && scr[1][Y] > 239 && scr[2][Y] > 239))
				{
					sptr++;
					continue;
				}

				setlen(polyf3, 4);		// F3
				if (sptr->TransType)
					setcode(polyf3, 0x22);
				else
					setcode(polyf3, 0x20);
				setRGB0(polyf3,sptr->R,sptr->G,sptr->B);
				setXY3(polyf3,scr[0][X],scr[0][Y],scr[1][X],scr[1][Y],scr[2][X],scr[2][Y]);
				MyaddPrim( db.ot,scr[0][Z]>>3,polyf3 );
				db.polyptr += sizeof(POLY_F3);
				if (sptr->TransType)
				{
					setDrawTPage((DR_TPAGE *)db.polyptr, 0, 0, getTPage(0, sptr->TransType-1, 0, 0));
					MyaddPrim( db.ot,scr[0][Z]>>3,(DR_TPAGE *)db.polyptr);
					db.polyptr += sizeof(DR_TPAGE);
				}
			}
			else if (slptr->Flags & SP_DEF)
			{
				if ( (scr[0][Z] < 32 || scr[0][Z] > 16384) ||
					(scr[0][X]+(w>>1) < 0) ||
					(scr[0][X]-(w>>1) > 383) ||
					(scr[0][Y]+(h>>1) < 0) ||
					(scr[0][Y]-(h>>1) > 239))
				{
					sptr++;
					continue;
				}

				if (slptr->Flags & SP_SCALE)
				{
					w = (slptr->Width<<slptr->Scalar)/scr[0][Z];
					h = (slptr->Height<<slptr->Scalar)/scr[0][Z];
					if (w > slptr->Width)
						w = slptr->Width;
					else if (w < 4)
						w = 4;
					if (h > slptr->Height)
						h = slptr->Height;
					else if (h < 4)
						h = 4;
				}
				else
				{
					w = slptr->Width;
					h = slptr->Height;
				}

				polyft4 = (POLY_FT4 *) db.polyptr;

				if (slptr->Flags & SP_ROTATE)
				{
					long	sin,cos;
					long sinx1,sinx2,siny1,siny2;
					long	cosx1,cosx2,cosy1,cosy2;

					sin = ((short)sincos_tbl[slptr->RotAng<<1]);
					cos = ((short)sincos_tbl[(slptr->RotAng<<1)+1]);
					sinx1 = ((-w>>1)*sin)>>12;
					sinx2 = ((w>>1)*sin)>>12;
					siny1 = ((-h>>1)*sin)>>12;
					siny2 = ((h>>1)*sin)>>12;
					cosx1 = ((-w>>1)*cos)>>12;
					cosx2 = ((w>>1)*cos)>>12;
					cosy1 = ((-h>>1)*cos)>>12;
					cosy2 = ((h>>1)*cos)>>12;

					x1 = sinx1-cosy1+scr[0][X];
					x2 = sinx2-cosy1+scr[0][X];
					x3 = sinx2-cosy2+scr[0][X];
					x4 = sinx1-cosy2+scr[0][X];
					y1 = cosx1+siny1+scr[0][Y];
					y2 = cosx2+siny1+scr[0][Y];
					y3 = cosx2+siny2+scr[0][Y];
					y4 = cosx1+siny2+scr[0][Y];

					setXY4(polyft4,x1,y1,x2,y2,x4,y4,x3,y3);
					slptr->RotAng += slptr->RotAdd;
					slptr->RotAng &= 4095;

				}
				else
				{
					x1 = scr[0][X]-(w>>1);
					x2 = scr[0][X]+(w>>1);
					y1 = scr[0][Y]-(h>>1);
					y2 = scr[0][Y]+(h>>1);
					setXY4(polyft4,x1,y1,x2,y1,x1,y2,x2,y2);
				}

				pSpriteInfo=psxspriteinfo+slptr->Def;
				setlen(polyft4, 9);		// POLY_FT4
				if (sptr->TransType)
					setcode(polyft4, 0x2e);
				else
					setcode(polyft4, 0x2c);
				setRGB0(polyft4,sptr->R,sptr->G,sptr->B);
				x1 = pSpriteInfo->u1;
				x2 = pSpriteInfo->u2;
				y1 = pSpriteInfo->v1;
				y2 = pSpriteInfo->v2;
				setUV4(polyft4,x1,y1,x2,y1,x1,y2,x2,y2);
				polyft4->tpage = (pSpriteInfo->tpage & (~96)) | ((sptr->TransType-1)<<5);
				polyft4->clut = pSpriteInfo->clut;

				MyaddPrim( db.ot,scr[0][Z]>>3,polyft4 );
				db.polyptr += sizeof(POLY_FT4);
			}
			else
			{
				polytile = (TILE *) db.polyptr;

				if ( (scr[0][Z] < 32 || scr[0][Z] > 16384) ||
					(scr[0][X]+(w>>1) < 0) ||
					(scr[0][X]-(w>>1) > 383) ||
					(scr[0][Y]+(h>>1) < 0) ||
					(scr[0][Y]-(h>>1) > 239))
				{
					sptr++;
					continue;
				}

				if (slptr->Flags & SP_SCALE)
				{
					w = (slptr->Width<<slptr->Scalar)/scr[0][Z];
					h = (slptr->Height<<slptr->Scalar)/scr[0][Z];
					if (w > slptr->Width)
						w = slptr->Width;
					else if (w < 1)
						w = 1;
					if (h > slptr->Height)
						h = slptr->Height;
					else if (h < 1)
						h = 1;
				}
				else
				{
					w = slptr->Width;
					h = slptr->Height;
				}

				setlen(polytile, 3);		// tile
				if (sptr->TransType)
					setcode(polytile, 0x62);
				else
					setcode(polytile, 0x60);
				setRGB0(polytile,sptr->R,sptr->G,sptr->B);
				setXY0(polytile,scr[0][X]-(w>>1),scr[0][Y]-(h>>1));
				setWH(polytile,w,h);
				MyaddPrim( db.ot,scr[0][Z]>>3,polytile );
				db.polyptr += sizeof(TILE);
				if (sptr->TransType)
				{
					setDrawTPage((DR_TPAGE *)db.polyptr, 0, 0, getTPage(0, sptr->TransType-1, 0, 0));
					MyaddPrim( db.ot,scr[0][Z]>>3,(DR_TPAGE *)db.polyptr);
					db.polyptr += sizeof(DR_TPAGE);
				}
			}
		}
		sptr++;
	}
}

void TriggerRicochetSpark(GAME_VECTOR *pos,long angle,long size)
{
	long		lp1;
	long		tangle;
	SPARKS	*sptr;
	SPARKLET	*slptr;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 255;
	sptr->sG = 32+(rand()&31);
	sptr->sB = 0;

	sptr->dR = 192;
	sptr->dG = 96+(rand()&63);
	sptr->dB = 0;

	sptr->ColFadeSpeed = 8;
	sptr->FadeToBlack = 4;
	sptr->sLife = sptr->Life = 16;
	sptr->TransType = COLADD;
	sptr->Dynamic = -1;

	slptr = &sptr->Sps;

//	for (lp1=0;lp1<sptr->NumSps;lp1++)
	{
		slptr->x = pos->x;
		slptr->y = pos->y;
		slptr->z = pos->z;
		slptr->Gravity = slptr->MaxYvel = 0;

		tangle = (angle + ((rand()&1023)-511)) & 4095;
		slptr->Zvel = (((((short)sincos_tbl[(tangle<<1)+1])))>>1);
		slptr->Xvel = (-((((short)sincos_tbl[(tangle<<1)])))>>1);
		slptr->Yvel = (rand()&511)-256;
		slptr->Friction = 3;
		slptr->Flags = SP_RICOCHET;
		slptr->sHeight = 32;
		slptr->dHeight = 2;

		slptr->x += slptr->Xvel>>3;	// Make ricochet spark long.
		slptr->y += slptr->Yvel>>3;
		slptr->z += slptr->Zvel>>3;

//		slptr++;
	}

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 255;
	sptr->sG = 32+(rand()&31);
	sptr->sB = 0;

	sptr->dR = 192;
	sptr->dG = 96+(rand()&63);
	sptr->dB = 0;

	sptr->ColFadeSpeed = 8;
	sptr->FadeToBlack = 8;
	sptr->sLife = sptr->Life = 24;
//	sptr->NumSps = (rand()&1)+3;
	sptr->TransType = COLADD;
	sptr->Dynamic = -1;

	slptr = &sptr->Sps;

//	for (lp1=0;lp1<sptr->NumSps;lp1++)
	{
		slptr->x = pos->x;
		slptr->y = pos->y;
		slptr->z = pos->z;
		tangle = (angle + ((rand()&2047)-1024)) & 4095;
		slptr->Zvel = (((((short)sincos_tbl[(tangle<<1)+1])))>>1);
		slptr->Xvel = (-((((short)sincos_tbl[(tangle<<1)])))>>1);
		slptr->Yvel = ((rand()&511)-384)<<1;
		slptr->Friction = 1;
		slptr->Flags = SP_SCALE;
		slptr->Scalar = 11;
		slptr->Gravity = ((rand()&31)) + (abs(slptr->Yvel>>6));
		slptr->sWidth = (rand()&3)+4;
		slptr->dWidth = (rand()&1)+1;
		slptr->sHeight = (rand()&3)+4;
		slptr->dHeight = (rand()&1)+1;
		slptr->MaxYvel = 0;
//		slptr++;
	}

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	lp1 = 128+(rand()&63);
	sptr->sR = lp1;
	sptr->sG = lp1;
	sptr->sB = lp1;

	sptr->dR = lp1>>1;
	sptr->dG = lp1>>1;
	sptr->dB = lp1>>1;

	sptr->ColFadeSpeed = 8;
	sptr->FadeToBlack = 16;
	sptr->sLife = sptr->Life = 28;
//	sptr->NumSps = (rand()&1)+3;
	sptr->TransType = COLSUB;
	sptr->Dynamic = -1;

	slptr = &sptr->Sps;

//	for (lp1=0;lp1<sptr->NumSps;lp1++)
	{
		slptr->x = pos->x;
		slptr->y = pos->y;
		slptr->z = pos->z;
		tangle = (angle + ((rand()&2047)-1023)) & 4095;
		slptr->Zvel = (((((short)sincos_tbl[(tangle<<1)+1])))>>1);
		slptr->Xvel = (-((((short)sincos_tbl[(tangle<<1)])))>>1);
		slptr->Yvel = ((rand()&511)-384);
		slptr->Friction = 1|32;
		slptr->Flags = SP_SCALE;
		slptr->Scalar = 11;
		slptr->Gravity = ((rand()&7)) + 4;
		slptr->sWidth = (rand()&3)+4;
		slptr->dWidth = (rand()&1)+1;
		slptr->sHeight = (rand()&3)+4;
		slptr->dHeight = (rand()&1)+1;
		slptr->MaxYvel = 0;
//		slptr++;
	}
}


void TriggerFlareSparks(long x, long y, long z,long xv,long yv,long zv)
{
	long		lp1;
	SPARKS	*sptr;
	SPARKLET	*slptr;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 255;
	sptr->sG = 255;
	sptr->sB = 255;

	sptr->dR = 255;
	sptr->dG = (rand()&127)+64;
	sptr->dB = 192-sptr->dG;//(rand()&31)+32;

	sptr->ColFadeSpeed = 3;
	sptr->FadeToBlack = 5;
	sptr->sLife = sptr->Life = 10;
//	sptr->NumSps = (rand()&3)+4;
	sptr->TransType = COLADD;
	sptr->Dynamic = -1;

	slptr = &sptr->Sps;
//	for (lp1=0;lp1<sptr->NumSps;lp1++)
	{
		slptr->x = x+(rand()&7)-3;
		slptr->y = y+(rand()&7)-3;
		slptr->z = z+(rand()&7)-3;
		slptr->Xvel = xv+((rand()&255)-128);
		slptr->Yvel = yv+((rand()&255)-128);
		slptr->Zvel = zv+((rand()&255)-128);
		slptr->Friction = 2|(2<<4);
		slptr->Flags = SP_SCALE;
		slptr->Scalar = 9;
		slptr->sWidth = (rand()&3)+4;
		slptr->dWidth = (rand()&1)+1;
		slptr->sHeight = (rand()&3)+4;
		slptr->dHeight = (rand()&1)+1;
		slptr->Gravity = slptr->MaxYvel = 0;
//		slptr++;
	}
}

void TriggerExplosionSparks(long x, long y, long z,long extra_trigs, long dyn)
{
	long		lp1,fric;
	SPARKS	*sptr;
	SPARKLET	*slptr;
	SP_DYNAMIC	*sdptr;

	uchar	extra_tab[6] = {	0,4,7,10	};

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 255;
	sptr->sG = 32+(rand()&15);
	sptr->sB = 0;

	sptr->dR = 192+(rand()&63);
	sptr->dG = 128+(rand()&63);
	sptr->dB = 32;

	sptr->ColFadeSpeed = 8;
	sptr->FadeToBlack = 16;
	sptr->sLife = sptr->Life = (rand()&7)+24;
//	if (dyn == -2)
//		sptr->NumSps = 4;
//	else
//		sptr->NumSps = 1;
	sptr->TransType = COLADD;

	fric = extra_tab[extra_trigs]+(rand()&7)-4;
	sptr->extras = extra_trigs|(fric<<3);

	sptr->Dynamic = dyn;

	if (dyn == -2)	// Set off a dynamic light controller for explosion.
	{
		sdptr = &spark_dynamics[0];
		for (lp1=0;lp1<32;lp1++)
		{
			if (!sdptr->On)
			{
				sdptr->On = 1;
				sdptr->Falloff = 4;
				sdptr->Flags = SD_EXPLOSION;
				sptr->Dynamic = lp1;
				break;
			}
			sdptr++;
		}

		if (lp1 == 32)
			sptr->Dynamic = -1;
	}

	slptr = &sptr->Sps;
//	for (lp1=0;lp1<sptr->NumSps;lp1++)
	{
		slptr->x = x+(rand()&31)-16;
		slptr->y = y+(rand()&31)-16;
		slptr->z = z+(rand()&31)-16;
		slptr->Xvel = ((rand()&4095)-2048);
		slptr->Yvel = ((rand()&4095)-2048);
		slptr->Zvel = ((rand()&4095)-2048);

		if (dyn==-2)
		{
			slptr->x += slptr->Xvel>>5;
			slptr->y += slptr->Yvel>>5;
			slptr->z += slptr->Zvel>>5;
		}

		if (!lp1)
			fric = 2;
		else
			fric = 3;

		slptr->Friction = fric|(fric<<4);

		if (rand()&1)
		{
			slptr->Flags = SP_SCALE|SP_DEF|SP_ROTATE;
			slptr->RotAng = rand()&4095;
			slptr->RotAdd = (rand()&255)-128;
		}
		else
			slptr->Flags = SP_SCALE|SP_DEF;

		slptr->Def = objects[EXPLOSION1].mesh_index;//+(rand()&1);
		slptr->Scalar = 10;
		slptr->Gravity = 0;
		fric = (rand()&15)+64;
		slptr->sWidth = fric;
		slptr->dWidth = fric<<1;
		fric += (rand()&15)+8;
 		slptr->sHeight = fric;
		slptr->dHeight = fric<<1;
		slptr->MaxYvel = 0;
//		slptr++;
	}

	if (extra_trigs == 0)
		TriggerExplosionSmokeEnd(x, y, z);
	else
		TriggerExplosionSmoke(x, y, z);
}

void TriggerExplosionSmokeEnd(long x, long y, long z)
{
	long		lp1,size;
	SPARKS	*sptr;
	SPARKLET	*slptr;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 144;
	sptr->sG = 144;
	sptr->sB = 144;

	sptr->dR = 64;
	sptr->dG = 64;
	sptr->dB = 64;

	sptr->ColFadeSpeed = 8;
	sptr->FadeToBlack = 64;
	sptr->sLife = sptr->Life = (rand()&31)+96;
	sptr->TransType = COLSUB;

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

//	sptr->NumSps = 1;

	slptr = &sptr->Sps;
//	for (lp1=0;lp1<sptr->NumSps;lp1++)
	{
		slptr->x = x+(rand()&31)-16;
		slptr->y = y+(rand()&31)-16;
		slptr->z = z+(rand()&31)-16;
		slptr->Xvel = ((rand()&4095)-2048)>>2;
		slptr->Yvel = (rand()&255)-128;
		slptr->Zvel = ((rand()&4095)-2048)>>2;
		slptr->Friction = 6;

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

		slptr->Def = objects[EXPLOSION1].mesh_index+1;//+(rand()&1);
		slptr->Scalar = 11;
		slptr->Gravity = -(rand()&3)-3;
		slptr->MaxYvel = -(rand()&3)-4;
		size = (rand()&31)+128;
		slptr->sWidth = size>>2;
		slptr->dWidth = size;
		size += (rand()&31)+32;
 		slptr->sHeight = size>>3;
		slptr->dHeight = size;
//		slptr++;
	}
}

void TriggerExplosionSmoke(long x, long y, long z)
{
	long		lp1,size;
	SPARKS	*sptr;
	SPARKLET	*slptr;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 144;
	sptr->sG = 144;
	sptr->sB = 144;

	sptr->dR = 64;
	sptr->dG = 64;
	sptr->dB = 64;

	sptr->ColFadeSpeed = 2;
	sptr->FadeToBlack = 8;
	sptr->sLife = sptr->Life = (rand()&3)+10;
	sptr->TransType = COLSUB;

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

//	sptr->NumSps = 1;

	slptr = &sptr->Sps;
//	for (lp1=0;lp1<sptr->NumSps;lp1++)
	{
		slptr->x = x+(rand()&511)-256;
		slptr->y = y+(rand()&511)-256;
		slptr->z = z+(rand()&511)-256;
		slptr->Xvel = ((rand()&4095)-2048)>>2;
		slptr->Yvel = (rand()&255)-128;
		slptr->Zvel = ((rand()&4095)-2048)>>2;
		slptr->Friction = 4;

		slptr->Flags = SP_SCALE|SP_DEF|SP_ROTATE;
		slptr->RotAng = rand()&4095;
		slptr->RotAdd = (rand()&15)+16;

		slptr->Def = objects[EXPLOSION1].mesh_index+1;//+(rand()&1);
		slptr->Scalar = 11;
		slptr->Gravity = -(rand()&3)-3;
		slptr->MaxYvel = -(rand()&3)-4;
		size = (rand()&31)+128;
		slptr->sWidth = size>>2;
		slptr->dWidth = size;
		size += (rand()&31)+32;
 		slptr->sHeight = size>>3;
		slptr->dHeight = size;
//		slptr++;
	}
}

long	GetFreeSpark()
{
	long		lp,eldest,free;
	SPARKS	*sptr;

	sptr = &spark[next_spark];
	for (free = next_spark,lp=0;lp<MAX_SPARKS;lp++)
	{
		if (!sptr->On)
		{
			next_spark = (free+1) & (MAX_SPARKS-1);
			return(free);
		}
		free = (free+1) & (MAX_SPARKS-1);
		sptr++;
	}

// Couldn't find a free spark slot so find the eldest.

	free = 0xfff;
	sptr = &spark[0];
	for (lp=0;lp<MAX_SPARKS;lp++)
	{
		if (sptr->Life < eldest && sptr->Dynamic == -1)	// Steal the eldest slot that doesn't control a dynamic light.
		{
			free = lp;
			eldest = sptr->Life;
		}
		sptr++;
	}

	next_spark = (free+1) & (MAX_SPARKS-1);
	return(free);
}


void TriggerAlertLight(long x, long y, long z, long r, long g, long b, long angle, long room_no)
{
	long			sin,cos;
	GAME_VECTOR	src,target;

	src.x = x;
	src.y = y;
	src.z = z;

	GetFloor(x, y, z, &room_no);
	src.room_number = room_no;

	sin = ((short)sincos_tbl[angle<<1])<<1;
	cos = ((short)sincos_tbl[(angle<<1)+1])<<1;

	target.x = x+sin;
	target.y = y;
	target.z = z+cos;

//	TriggerFlareSparks(target.x, target.y, target.z,0,0,0);
//	TriggerDynamic(target.x, target.y, target.z, 8, r, g, b);

	if (!(LOS(&src,&target)) )	// Hit anything?
	{
		TriggerDynamic(target.x, target.y, target.z, 16, r, g, b);
//		TriggerFlareSparks(target.x, target.y, target.z,0,0,0);
	}
}
