#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"
#include "../spec_psx/memory.h"
#else
#include "../specific/stypes.h"
#include <sys\types.h>

typedef unsigned char uchar;

#endif

#include "objects.h"
#include "lara.h"
#include "sound.h"
#include "effect2.h"
#include "lot.h"
#include "sphere.h"
#include "control.h"
#include "traps.h"
#include "pickup.h"
#include "setup.h"

static void TriggerFireBallFlame(sint16 fx_number, long type, long xv, long yv, long zv);
static void TriggerFireBall(ITEM_INFO *item, long type, PHD_VECTOR *pos1, sint16 room_number, sint16 angle, long zdspeed);
static void TriggerTonyFlame(sint16 item_number, long hand);

#ifdef PSX_VERSION
static void DrawTonyBossShield(ITEM_INFO *item);
static void DrawExplosionRings();
#endif


static void TonyBossDie(sint16 item_number);
static void ExplodeTonyBoss(ITEM_INFO *item);


static void TonyBossFlipMap(void);

extern sint32 GetRandomControl();
extern sint32 GetRandomDraw();

extern long wibble;
extern short rcossin_tbl[];
extern int LOS(GAME_VECTOR *start, GAME_VECTOR *target);
extern char exit_message[];

/*********************************** TYPE DEFINITIONS ****************************************/

//#define TONY_NOT_GOOD

enum 	{	ROCKZAPPL=0,		// To ceiling from left hand
			ROCKZAPPR,		// To ceiling from right hand
			ZAPP,			// From right hand.
			DROPPER,			// From ceiling.
			ROCKZAPPDEBRIS,	// Small bits from ceiling explosions.
			ZAPPDEBRIS,		// Small bits from hand flame explosions.
			DROPPERDEBRIS,		// Small bits from droppers explosions.
};

enum tonyboss_anims {TONYBOSS_WAIT, TONYBOSS_RISE, TONYBOSS_FLOAT, TONYBOSS_ZAPP, TONYBOSS_ROCKZAPP, TONYBOSS_BIGBOOM, TONYBOSS_DEATH	};

#define TONYBOSS_TURN 		(ONE_DEGREE*2)
#define TONYBOSS_DIE_ANIM 	6
#define TONYBOSS_HITS		100

#define MAX_TONY_TRIGGER_RANGE 0x4000

/*---------------------------------------------------------------------------
 *	Globals
\*--------------------------------------------------------------------------*/

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

void InitialiseTonyBoss(sint16 item_number);
void TonyBossControl(sint16 item_number);
void S_DrawTonyBoss(ITEM_INFO *item);
void ControlTonyFireBall(sint16 fx_number);

void *func[] = {
	&InitialiseTonyBoss,
	&TonyBossControl,
	&S_DrawTonyBoss,
	&ControlTonyFireBall,
};

#endif

/*---------------------------------------------------------------------------
 *	Locals
\*--------------------------------------------------------------------------*/

static	long		death_radii[5];
static	long		death_heights[5];
static	long		radii[5] = {	200,400,500,500,475	};
static	long		heights[5] = {	-1536,-1280,-832,-384,0	};
static	long		dradii[5] = {	100<<4,350<<4,400<<4,350<<4,100<<4	};
static	long		dheights1[5] = {	-1536-(768<<3),-1152-(384<<3),-768,-384+(384<<3),0+(768<<3)	};
static	long		dheights2[5] = {	-1536,-1152,-768,-384,0	};

typedef struct
{
	short 	x;
	short 	y;
	short 	z;
	uchar	rsub;
	uchar	gsub;
	uchar	bsub;
	uchar	pad[3];
	long		rgb;

} SHIELD_POINTS;

typedef struct
{
	short 	x;
	short 	z;
	long		rgb;

} EXPLOSION_VERTS;

typedef struct
{
	short on;
	short life;	// 0 - 32.
	short speed;
	short radius;	// Width is 1/4 of radius.
	short xrot;
	short zrot;
	long	x;
	long	y;
	long	z;
	EXPLOSION_VERTS	verts[16];

} EXPLOSION_RING;

static SHIELD_POINTS	TonyBossShield[40];	// x,y,z,rgb.
static EXPLOSION_RING	ExpRings[7];

/*---------------------------------------------------------------------------
 *	Local Functions
\*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
 *	Public Functions
\*--------------------------------------------------------------------------*/

void TonyBossControl(sint16 item_number)
{
	ITEM_INFO *item;
	CREATURE_INFO *tonyboss;
	AI_INFO info;
	PHD_VECTOR	pos1;

	sint16 angle, head, torso_x, torso_y, tilt,lp;
	long	rnd;

	if (!CreatureActive(item_number))
		return;

	item = &items[item_number];
	tonyboss = (CREATURE_INFO *)item->data;
	head = torso_y = torso_x = angle = tilt = 0;

	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != TONYBOSS_DEATH)
		{
			item->anim_number = objects[item->object_number].anim_index + TONYBOSS_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = TONYBOSS_DEATH;
		}

		if (item->frame_number - anims[item->anim_number].frame_base > 110)
		{
			item->frame_number = anims[item->anim_number].frame_base + 110;
			item->mesh_bits = 0;	// Don't draw any of the boss while I'm killing it.
			if (bossdata.explode_count == 0)
			{
				bossdata.ring_count = 0;
				for (lp=0;lp<6;lp++)
				{
					ExpRings[lp].on = 0;
					ExpRings[lp].life = 32;
					ExpRings[lp].radius = 512;
					ExpRings[lp].speed = 128+(lp<<5);
					ExpRings[lp].xrot = ((GetRandomControl()&511)-256)&4095;
					ExpRings[lp].zrot = ((GetRandomControl()&511)-256)&4095;
				}

				if (bossdata.dropped_icon == 0)
				{
					BossDropIcon(item_number);
					bossdata.dropped_icon = 1;
				}
			}

			if (bossdata.explode_count < 256)
				bossdata.explode_count++;
			if (	bossdata.explode_count > 128 && bossdata.ring_count == 6 && ExpRings[5].life == 0)
			{
				TonyBossDie(item_number);
				bossdata.dead = 1;
			}
			else
				ExplodeTonyBoss(item);
			return;
		}
	}
	else
	{
		if (item->item_flags[3] != 2)	// Still invincible ?
			item->hit_points = TONYBOSS_HITS;

		CreatureAIInfo(item, &info);

		if (!item->item_flags[3])	// Is she close enough yet ?
		{
			long	dx,dz;
			dx = item->pos.x_pos - lara_item->pos.x_pos;
			dz = item->pos.z_pos - lara_item->pos.z_pos;
			if ((SQUARE(dx)+SQUARE(dz)) <  SQUARE(5120))
				item->item_flags[3] = 1;
			angle = 0;
		}
		else
		{
			tonyboss->target.x = lara_item->pos.x_pos;
			tonyboss->target.z = lara_item->pos.z_pos;
			angle = CreatureTurn(item, tonyboss->maximum_turn);
		}

		if (info.ahead)
			head = info.angle;

		switch (item->current_anim_state)
		{
			case TONYBOSS_WAIT:	// Waiting.
				tonyboss->maximum_turn = 0;

				if (item->goal_anim_state != TONYBOSS_RISE && item->item_flags[3])
					item->goal_anim_state = TONYBOSS_RISE;
				break;

			case TONYBOSS_RISE:	// Rising.
				if (item->frame_number - anims[item->anim_number].frame_base > 16)
					tonyboss->maximum_turn = TONYBOSS_TURN;
				else
					tonyboss->maximum_turn = 0;
				break;

			case TONYBOSS_FLOAT:	// Rising.
				torso_y = info.angle;
				torso_x = info.x_angle;

				tonyboss->maximum_turn = TONYBOSS_TURN;

				if (!bossdata.explode_count)
				{
					if (item->goal_anim_state != TONYBOSS_BIGBOOM && item->item_flags[3] != 2)
					{
						item->goal_anim_state = TONYBOSS_BIGBOOM;
						tonyboss->maximum_turn = 0;
					}

					if (item->goal_anim_state != TONYBOSS_ROCKZAPP && item->item_flags[3] == 2)
					{
						if (!(wibble&255) && item->item_flags[0] == 0)
						{
							item->goal_anim_state = TONYBOSS_ROCKZAPP;
							item->item_flags[0] = 1;
						}
					}

					if (item->goal_anim_state != TONYBOSS_ZAPP && item->goal_anim_state != TONYBOSS_ROCKZAPP && item->item_flags[3] == 2)
					{
						if (!(wibble&255) && item->item_flags[0] == 1)
	//					if (!(wibble&255))
						{
							item->goal_anim_state = TONYBOSS_ZAPP;
							item->item_flags[0] = 0;
						}
					}
				}

				break;

			case TONYBOSS_ROCKZAPP:	// Shooting at ceiling.
				torso_y = info.angle;
				torso_x = info.x_angle;

				tonyboss->maximum_turn = 0;

				if (item->frame_number - anims[item->anim_number].frame_base == 40)
				{
					TriggerFireBall(item,ROCKZAPPL,(PHD_VECTOR *) NULL,item->room_number,0,0);
					TriggerFireBall(item,ROCKZAPPR,(PHD_VECTOR *) NULL,item->room_number,0,0);
				}
				break;

			case TONYBOSS_ZAPP:	// Shooting at ceiling.
				torso_y = info.angle;
				torso_x = info.x_angle;

				tonyboss->maximum_turn = TONYBOSS_TURN>>1;

				if (item->frame_number - anims[item->anim_number].frame_base == 28)
					TriggerFireBall(item,ZAPP,(PHD_VECTOR *) NULL,item->room_number,item->pos.y_rot,0);
				break;

			case TONYBOSS_BIGBOOM:	// Changing room.
				tonyboss->maximum_turn = 0;
				if (item->frame_number - anims[item->anim_number].frame_base == 56)
				{
					item->item_flags[3] = 2;
					bossdata.explode_count = 1;
				}
				break;

			default:
				break;
		}
	}


	if (item->current_anim_state == TONYBOSS_ROCKZAPP || item->current_anim_state == TONYBOSS_ZAPP || item->current_anim_state == TONYBOSS_BIGBOOM)
	{
		long	bright,r,g,b;

		bright = item->frame_number - anims[item->anim_number].frame_base;
		if (bright > 16)
		{
			bright = anims[item->anim_number].frame_end - item->frame_number;
			if (bright > 16)
				bright = 16;
		}

		rnd = GetRandomControl();
		r=31-((rnd>>4)&3);
		g=24-((rnd>>6)&3);
		b=rnd&7;
		r = (r*bright)>>4;
		g = (g*bright)>>4;
		b = (b*bright)>>4;

		pos1.x = pos1.y = pos1.z = 0;
		GetJointAbsPosition( &items[item_number], &pos1, 10);
		TriggerDynamic(pos1.x,pos1.y,pos1.z,12,r,g,b);
		TriggerTonyFlame(item_number, SPN_TONYHANDRFLAME);

		if (item->current_anim_state == TONYBOSS_ROCKZAPP || item->current_anim_state == TONYBOSS_BIGBOOM)
		{
			pos1.x = pos1.y = pos1.z = 0;
			GetJointAbsPosition( &items[item_number], &pos1, 13);
			TriggerDynamic(pos1.x,pos1.y,pos1.z,12,r,g,b);
			TriggerTonyFlame(item_number, SPN_TONYHANDLFLAME);
		}
	}

	if (bossdata.explode_count && item->hit_points > 0)
	{
		ExplodeTonyBoss(item);
		bossdata.explode_count++;
		if (bossdata.explode_count == 32)
//##			TonyBossFlipMap();
			FlipMap();
		if (bossdata.explode_count > 64)
			bossdata.explode_count = bossdata.ring_count = 0;
	}

	/* Actually do animation allowing for collisions */
	CreatureJoint(item, 0, torso_y >> 1);
	CreatureJoint(item, 1, torso_x);
	CreatureJoint(item, 2, torso_y >> 1);

	CreatureAnimation(item_number, angle, 0);
}

void S_DrawTonyBoss(ITEM_INFO *item)
{
	DrawAnimatingItem(item);

	if (bossdata.explode_count && item->hit_points <= 0)
		DrawExplosionRings();

	if (bossdata.explode_count && bossdata.explode_count <=64)
		DrawTonyBossShield(item);
}

static void TonyBossDie(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	item->collidable = 0;
	item->hit_points = DONT_TARGET;
	KillItem(item_number);
	DisableBaddieAI(item_number);
	item->flags |= ONESHOT;	// Don't want bad guys triggered ever again
}

void InitialiseTonyBoss(sint16 item_number)
{
	SHIELD_POINTS	*shptr;
	long		lp,lp1,angle,y;

	items[item_number].item_flags[3] = bossdata.explode_count = bossdata.ring_count =
	bossdata.dropped_icon = bossdata.dead = 0;

// Initialise shield coordinates.

	shptr = &TonyBossShield[0];	// x,y,z,rgb.

	for (lp=0;lp<5;lp++)
	{
		y = heights[lp];
		angle = 0;
		for (lp1=0;lp1<8;lp1++)
		{
			shptr->x = (rcossin_tbl[angle<<1]*radii[lp])>>11;
			shptr->y = y;
			shptr->z = (rcossin_tbl[(angle<<1)+1]*radii[lp])>>11;
			shptr->rgb = 0;	// rgb.
			angle += 512;
			shptr++;
		}
	}
}



#ifdef PSX_VERSION

static void DrawTonyBossShield(ITEM_INFO *item)
{
	POLY_GT4	*polygt4;
	PSXSPRITESTRUCT* pSpriteInfo;
	SHIELD_POINTS	*shptr,*shptr2;
	short	*scrxy,*scrxy2;	// X,Y
	long		*scrz,*scrz2;	// Z
	short	*TempMesh;
	long		lp,u1,u2,v1,v2;

	mPushMatrix();

//	SetRotMatrix((MATRIX*) Matrix);
//	SetTransMatrix((MATRIX*) Matrix);
//	mTranslateXYZ(item->pos.x_pos,item->pos.y_pos,item->pos.z_pos);
	mTranslateAbsXYZ(item->pos.x_pos,item->pos.y_pos,item->pos.z_pos);

	scrxy = (short *) 0x1f800000;	// Set pointer to scratch pad.
	scrz = (long *) 0x1f800100;	// Set pointer to scratch pad.
	TempMesh = (short *) 0x1f800200;	// Set pointer to scratch pad.

	shptr = &TonyBossShield[0];

	for (lp=0;lp<40;lp++)
	{
		long	r,g,b,rgb;

		TempMesh[0] = shptr->x;
		TempMesh[1] = shptr->y;
		TempMesh[2] = shptr->z;

		if (lp>=8 && lp<=31)
		{
			rgb = shptr->rgb;
			r = rgb & 255;
			g = (rgb >> 8) & 255;
			b = (rgb >> 16) & 255;

			if (rgb)
			{
			  	r -= shptr->rsub;
			  	g -= shptr->gsub;
			  	b -= shptr->bsub;
				if (r < 0)
					r = 0;
				if (g < 0)
					g = 0;
				if (b < 0)
					b = 0;

				shptr->rgb = r|(g<<8)|(b<<16);
			}
		}

		shptr++;

		gte_ldv0(TempMesh);
		gte_rtps();
		gte_stsxy(scrxy);
		gte_stsz(scrz);
		scrxy+=2;
		scrz++;
	}

	shptr = &TonyBossShield[0];
	shptr2 = &TonyBossShield[8];

	scrxy = (short *) 0x1f800000;	// Set pointer to scratch pad.
	scrz = (long *) 0x1f800100;	// Set pointer to scratch pad.
	scrxy2 = (short *) 0x1f800020;	// Set pointer to scratch pad.
	scrz2 = (long *) 0x1f800120;	// Set pointer to scratch pad.

	polygt4 = (POLY_GT4 *)db.polyptr;

	for (lp=0;lp<4;lp++)
	{
	 	long		x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,rgb1,rgb2,rgb3,rgb4,lp1;

		x1 = *scrxy++;
		y1 = *scrxy++;
		z1 = *scrz++;
		x3 = *scrxy2++;
		y3 = *scrxy2++;
		z3 = *scrz2++;
		rgb1 = shptr->rgb;
		rgb3 = shptr2->rgb;
		shptr++;
		shptr2++;

  		pSpriteInfo=psxspriteinfo+objects[EXPLOSION1].mesh_index+18+((lp+(wibble>>3))&7);
		u1 = pSpriteInfo->u1;
		u2 = pSpriteInfo->u2;
		v1 = pSpriteInfo->v1;
		v2 = pSpriteInfo->v2;

		for (lp1=0;lp1<8;lp1++)
		{
			if ((char *)polygt4 >= db.polybuf_limit)
			{
				db.polyptr = (char *)polygt4;
				mPopMatrix();
				return;
			}

			if (lp1 != 7)
			{
				x2 = *scrxy++;
				y2 = *scrxy++;
				z2 = *scrz++;
				x4 = *scrxy2++;
				y4 = *scrxy2++;
				z4 = *scrz2++;
				rgb2 = shptr->rgb;
				rgb4 = shptr2->rgb;
				shptr++;
				shptr2++;
			}
			else
			{
				x2 = *(scrxy-16);
				y2 = *(scrxy-15);
				z2 = *(scrz-8);
				x4 = *(scrxy2-16);
				y4 = *(scrxy2-15);
				z4 = *(scrz2-8);
				rgb2 = (shptr-8)->rgb;
				rgb4 = (shptr)->rgb;
			}

			z1 += z2+z3+z4;
			z1 >>= 5;

			if (z1>32 && z1 < 0xa00 && (rgb1 || rgb2 || rgb3 || rgb4))
			{
				setlen(polygt4, 12);
				setcode(polygt4, 0x3e);
				setRGB0(polygt4,rgb1&0xff,(rgb1>>8)&0xff,(rgb1>>16)&0xff);
				setRGB1(polygt4,rgb2&0xff,(rgb2>>8)&0xff,(rgb2>>16)&0xff);
				setRGB2(polygt4,rgb3&0xff,(rgb3>>8)&0xff,(rgb3>>16)&0xff);
				setRGB3(polygt4,rgb4&0xff,(rgb4>>8)&0xff,(rgb4>>16)&0xff);
				setUV4(polygt4,u1,v1,u2,v1,u1,v2,u2,v2);
				setXY4(polygt4,x1,y1,x2,y2,x3,y3,x4,y4);
				polygt4->clut = pSpriteInfo->clut;
				polygt4->tpage = (pSpriteInfo->tpage & (~96)) | (1<<5);	// Set colour addition.
				MyaddPrim(db.ot, z1, polygt4);
				polygt4++;
			}

			x1 = x2;
			y1 = y2;
			z1 = z2;
			x3 = x4;
			y3 = y4;
			z3 = z4;
			rgb1 = rgb2;
			rgb3 = rgb4;
		}
	}

	db.polyptr = (uchar *) polygt4;
	mPopMatrix();
}

#endif

static void ExplodeTonyBoss(ITEM_INFO *item)
{
	SHIELD_POINTS	*shptr;
	long		lp,lp1,angle,y,r,g,b;

	if (	item->hit_points <= 0 &&
		(bossdata.explode_count == 1 ||
		bossdata.explode_count == 15 ||
		bossdata.explode_count == 25 ||
		bossdata.explode_count == 35 ||
		bossdata.explode_count == 45 ||
		bossdata.explode_count == 55))	// Trigger explosion & ring ?
	{
		long x,y,z;

		x = item->pos.x_pos + ((GetRandomDraw()&1023)-512);
		y = item->pos.y_pos - (GetRandomDraw()&1023) - 256;
		z = item->pos.z_pos + ((GetRandomDraw()&1023)-512);

		ExpRings[bossdata.ring_count].x = x;
		ExpRings[bossdata.ring_count].y = y;
		ExpRings[bossdata.ring_count].z = z;
		ExpRings[bossdata.ring_count].on = 1;
		bossdata.ring_count++;

		TriggerExplosionSparks(x, y, z,3,-2,0,0);	// -2 = Set off a dynamic light controller.
		for (lp=0;lp<2;lp++)
			TriggerExplosionSparks(x, y, z,3,-1,0, 0);

	    	SoundEffect( 76, &item->pos,PITCH_SHIFT|0x800000);
	}

// Adjust shield coordinates.

	for (lp=0;lp<5;lp++)
	{
		if (bossdata.explode_count < 128)	// Expand.
		{
			death_radii[lp] = (dradii[lp] >> 4) + (((dradii[lp]) * bossdata.explode_count) >> 7);
			death_heights[lp] = dheights2[lp] + (((dheights1[lp]-dheights2[lp]) * bossdata.explode_count) >> 7);
		}
	}

// Setup shield coordinates.

	if (bossdata.explode_count <= 64)
	{
		shptr = &TonyBossShield[0];	// x,y,z,rgb.
		for (lp=0;lp<5;lp++)
		{
			y = death_heights[lp];
			angle = (wibble<<3) & 511;
			for (lp1=0;lp1<8;lp1++)
			{
				shptr->x = (rcossin_tbl[angle<<1] * death_radii[lp])>>11;
				shptr->y = y;
				shptr->z = (rcossin_tbl[(angle<<1)+1] * death_radii[lp])>>11;

				if (lp!=0 && lp!=4 && bossdata.explode_count < 64)
				{
					r = (GetRandomDraw()&31)+224;
					g = (r>>2) + (GetRandomDraw()&63);
					b = (GetRandomDraw()&63);
					if (item->hit_points > 0)
					{
						r = (r * (128 - bossdata.explode_count)) >> 7;
						g = (g * (128 - bossdata.explode_count)) >> 7;
						b = (b * (128 - bossdata.explode_count)) >> 7;
					}
					else
					{
						r = (r * (64 - bossdata.explode_count)) >> 6;
						g = (g * (64 - bossdata.explode_count)) >> 6;
						b = (b * (64 - bossdata.explode_count)) >> 6;
					}
					shptr->rgb = r|(g<<8)|(b<<16);
				}
				else
					shptr->rgb = 0;
				angle += 512;
				angle &= 4095;
				shptr++;
			}
		}
	}
}

#ifdef PSX_VERSION

static void DrawExplosionRings()
{
	PSXSPRITESTRUCT	*pSpriteInfo;
	EXPLOSION_VERTS	*evptr,*evptr2;
	EXPLOSION_RING		*erptr;
	POLY_GT4			*polygt4;
	long		rlp,lp,lp1;
	long		angle,rad,r,g,b;
	short	*scrxy,*scrxy2;	// X,Y
	long		*scrz,*scrz2;	// Z
	short	*TempMesh;
	long		u1,u2,v1,v2;

	for (rlp=0;rlp<6;rlp++)
	{
		erptr = &ExpRings[rlp];
		if (erptr->on == 0)
		{
			erptr++;
			continue;
		}

		erptr->life--;
		if (erptr->life == 0)
		{
			erptr->on = 0;
			erptr++;
			continue;
		}

		mPushMatrix();

//		SetRotMatrix((MATRIX*) Matrix);
//		SetTransMatrix((MATRIX*) Matrix);
//		mTranslateXYZ(erptr->x,erptr->y,erptr->z);
		mTranslateAbsXYZ(erptr->x,erptr->y,erptr->z);
		mRotZ(erptr->zrot);
		mRotX(erptr->xrot);
//		SetRotMatrix((MATRIX*) Matrix);
//		SetTransMatrix((MATRIX*) Matrix);

		erptr->radius += erptr->speed;
//		erptr->speed += 8;

		evptr = &erptr->verts[0];
		rad = erptr->radius;	// Outer ring.

		scrxy = (short *) 0x1f800000;	// Set pointer to scratch pad.
		scrz = (long *) 0x1f800100;	// Set pointer to scratch pad.
		TempMesh = (short *) 0x1f800200;	// Set pointer to scratch pad.

		for (lp=0;lp<2;lp++)
		{
			angle = (wibble<<3) & 511;

			for (lp1=0;lp1<8;lp1++)
			{
				evptr->x = (rcossin_tbl[angle<<1] * rad) >> 12;
				evptr->z = (rcossin_tbl[(angle<<1)+1] * rad) >> 12;

				r = (GetRandomDraw()&31)+224;
				g = (r>>2) + (GetRandomDraw()&63);
				b = (GetRandomDraw()&63);
				r = (r * erptr->life) >> 5;
				g = (g * erptr->life) >> 5;
				b = (b * erptr->life) >> 5;
				evptr->rgb = r|(g<<8)|(b<<16);

				angle += 512;
				angle &= 4095;

				TempMesh[0] = evptr->x;
				TempMesh[1] = 0;
				TempMesh[2] = evptr->z;

				gte_ldv0(TempMesh);
				gte_rtps();
				gte_stsxy(scrxy);
				gte_stsz(scrz);
				scrxy+=2;
				scrz++;
				evptr++;
			}
			//rad -= rad>>2;		// Inner ring.
			rad >>= 1;		// Inner ring.
		}

		evptr = &erptr->verts[0];
		evptr2 = &erptr->verts[8];

		scrxy = (short *) 0x1f800000;		// Set pointer to scratch pad.
		scrz = (long *) 0x1f800100;		// Set pointer to scratch pad.
		scrxy2 = (short *) 0x1f800020;	// Set pointer to scratch pad.
		scrz2 = (long *) 0x1f800120;		// Set pointer to scratch pad.

		polygt4 = (POLY_GT4 *)db.polyptr;

//		for (lp=0;lp<4;lp++)
		{
	 		long		x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,rgb1,rgb2,rgb3,rgb4;

			x1 = *scrxy++;
			y1 = *scrxy++;
			z1 = *scrz++;
			x3 = *scrxy2++;
			y3 = *scrxy2++;
			z3 = *scrz2++;
			rgb1 = evptr->rgb;
			rgb3 = evptr2->rgb;
			evptr++;
			evptr2++;

			pSpriteInfo=psxspriteinfo+objects[EXPLOSION1].mesh_index+4+((wibble>>4)&3);
  			//pSpriteInfo=psxspriteinfo+objects[EXPLOSION1].mesh_index+18+((lp+(wibble>>3))&7);
			u1 = pSpriteInfo->u1;
			u2 = pSpriteInfo->u2;
			v1 = pSpriteInfo->v1;
			v2 = pSpriteInfo->v2;

			for (lp1=0;lp1<8;lp1++)
			{
				if ((char *)polygt4 >= db.polybuf_limit)
				{
					db.polyptr = (char *)polygt4;
					mPopMatrix();
					return;
				}

				if (lp1 != 7)
				{
					x2 = *scrxy++;
					y2 = *scrxy++;
					z2 = *scrz++;
					x4 = *scrxy2++;
					y4 = *scrxy2++;
					z4 = *scrz2++;
					rgb2 = evptr->rgb;
					rgb4 = evptr2->rgb;
					evptr++;
					evptr2++;
				}
				else
				{
					x2 = *(scrxy-16);
					y2 = *(scrxy-15);
					z2 = *(scrz-8);
					x4 = *(scrxy2-16);
					y4 = *(scrxy2-15);
					z4 = *(scrz2-8);
					rgb2 = (evptr-8)->rgb;
					rgb4 = (evptr)->rgb;
				}

				z1 += z2+z3+z4;
				z1 >>= 5;

				if (z1>32 && z1 < 0xa00 && (rgb1 || rgb2 || rgb3 || rgb4))
				{
					setlen(polygt4, 12);
					setcode(polygt4, 0x3e);
					setRGB0(polygt4,rgb1&0xff,(rgb1>>8)&0xff,(rgb1>>16)&0xff);
					setRGB1(polygt4,rgb2&0xff,(rgb2>>8)&0xff,(rgb2>>16)&0xff);
					setRGB2(polygt4,rgb3&0xff,(rgb3>>8)&0xff,(rgb3>>16)&0xff);
					setRGB3(polygt4,rgb4&0xff,(rgb4>>8)&0xff,(rgb4>>16)&0xff);
					setUV4(polygt4,u1,v1,u2,v1,u1,v2,u2,v2);
					setXY4(polygt4,x1,y1,x2,y2,x3,y3,x4,y4);
					polygt4->clut = pSpriteInfo->clut;
					polygt4->tpage = (pSpriteInfo->tpage & (~96)) | (1<<5);	// Set colour addition.
					MyaddPrim(db.ot, z1, polygt4);
					polygt4++;
				}

				x1 = x2;
				y1 = y2;
				z1 = z2;
				x3 = x4;
				y3 = y4;
				z3 = z4;
				rgb1 = rgb2;
				rgb3 = rgb4;
			}
		}

		db.polyptr = (uchar *) polygt4;
		mPopMatrix();
	}
}

#endif


static void TriggerTonyFlame(sint16 item_number, long hand)
{
	long		size;
	SPARKS	*sptr;
	long		dx,dz;

	dx = lara_item->pos.x_pos - items[item_number].pos.x_pos;
	dz = lara_item->pos.z_pos - items[item_number].pos.z_pos;

	if (dx < -MAX_TONY_TRIGGER_RANGE || dx > MAX_TONY_TRIGGER_RANGE || dz < -MAX_TONY_TRIGGER_RANGE || dz > MAX_TONY_TRIGGER_RANGE)
		return;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 255;
	sptr->sG = 48+(GetRandomControl()&31);
	sptr->sB = 48;

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

	sptr->ColFadeSpeed = 12 + (GetRandomControl()&3);
	sptr->FadeToBlack = 8;
	sptr->sLife = sptr->Life = (GetRandomControl()&7)+24;

	sptr->TransType = COLADD;

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

	sptr->x = ((GetRandomControl()&15)-8);
	sptr->y = 0;
	sptr->z = ((GetRandomControl()&15)-8);

	sptr->Xvel = ((GetRandomControl()&255)-128);
	sptr->Yvel = -(GetRandomControl()&15)-16;
	sptr->Zvel = ((GetRandomControl()&255)-128);
	sptr->Friction = 5;

	if (GetRandomControl()&1)
	{
		sptr->Flags = SP_SCALE|SP_DEF|SP_ROTATE|SP_EXPDEF|SP_ITEM|SP_NODEATTATCH;
		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|SP_ITEM|SP_NODEATTATCH;

	sptr->Gravity = -(GetRandomControl()&31)-16;
	sptr->MaxYvel = -(GetRandomControl()&7)-16;
	sptr->FxObj = item_number;
	sptr->NodeNumber = hand;

	sptr->Def = objects[EXPLOSION1].mesh_index;
	sptr->Scalar = 1;
	size = (GetRandomControl()&31)+64;
	sptr->Width = sptr->sWidth = size;
 	sptr->Height = sptr->sHeight = size;
	sptr->dWidth = size>>2;
	sptr->dHeight = size>>2;
}

static void TriggerFireBallFlame(sint16 fx_number, long type, long xv, long yv, long zv)
{
	SPARKS	*sptr;
	long		size;
	long		dx,dz;

	dx = lara_item->pos.x_pos - effects[fx_number].pos.x_pos;
	dz = lara_item->pos.z_pos - effects[fx_number].pos.z_pos;

	if (dx < -MAX_TONY_TRIGGER_RANGE || dx > MAX_TONY_TRIGGER_RANGE || dz < -MAX_TONY_TRIGGER_RANGE || dz > MAX_TONY_TRIGGER_RANGE)
		return;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 255;
	sptr->sG = 48+(GetRandomControl()&31);
	sptr->sB = 48;

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

	sptr->ColFadeSpeed = 12 + (GetRandomControl()&3);
	sptr->FadeToBlack = 8;
	sptr->sLife = sptr->Life = (GetRandomControl()&7)+24;

	sptr->TransType = COLADD;

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

	sptr->x = ((GetRandomControl()&15)-8);
	sptr->y = 0;
	sptr->z = ((GetRandomControl()&15)-8);

	sptr->Xvel = xv + ((GetRandomControl()&255)-128);
	sptr->Yvel = yv;
	sptr->Zvel = zv + ((GetRandomControl()&255)-128);
	sptr->Friction = 5;

	if (GetRandomControl()&1)
	{
		sptr->Flags = SP_SCALE|SP_DEF|SP_ROTATE|SP_EXPDEF|SP_FX;
		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|SP_FX;
	}

	sptr->FxObj = fx_number;

	sptr->Def = objects[EXPLOSION1].mesh_index;
	sptr->Scalar = 1;
	size = (GetRandomControl()&31)+64;
	sptr->Width = sptr->sWidth = size;
 	sptr->Height = sptr->sHeight = size;
	sptr->dWidth = size>>2;
	sptr->dHeight = size>>2;

	if (type == ROCKZAPPL || type == ROCKZAPPR)
	{
		sptr->Gravity = (GetRandomControl()&31)+16;
		sptr->MaxYvel = (GetRandomControl()&15)+48;
		sptr->Yvel = -sptr->Yvel<<4;
		sptr->Scalar = 2;
	}
	else if (type == ROCKZAPPDEBRIS || type == ZAPPDEBRIS || type == DROPPERDEBRIS)
		sptr->Gravity = sptr->MaxYvel = 0;
	else if (type == DROPPER)
	{
		sptr->Gravity = -(GetRandomControl()&31)-16;
		sptr->MaxYvel = -(GetRandomControl()&31)-64;
		sptr->Yvel = sptr->Yvel<<4;
		sptr->Scalar = 2;
	}
	else if (type == ZAPP)
	{
		sptr->Gravity = sptr->MaxYvel = 0;
		sptr->Scalar = 2;
	}
}

static void TriggerFireBall(ITEM_INFO *item, long type, PHD_VECTOR *pos1, sint16 room_number, sint16 angle, long zdspeed)
{
	FX_INFO 	*fx;
	PHD_VECTOR pos;
	sint16 	fx_number;
	long		speed,fallspeed;

	if (type == ROCKZAPPL)
	{
		pos.x = pos.y = pos.z = 0;
		GetJointAbsPosition(item,&pos,10);
		angle = item->pos.y_rot;
		speed = 0;
		fallspeed = -16;
	}
	else if (type == ROCKZAPPR)
	{
		pos.x = pos.y = pos.z = 0;
		GetJointAbsPosition(item,&pos,13);
		angle = item->pos.y_rot;
		speed = 0;
		fallspeed = -16;
	}
	else if (type == ZAPP)
	{
		pos.x = pos.y = pos.z = 0;
		GetJointAbsPosition(item,&pos,13);
		speed = 160;
		fallspeed = -(GetRandomControl()&7)-32;
	}
	else if (type == ROCKZAPPDEBRIS)
	{
		pos.x = pos1->x;
		pos.y = pos1->y;
		pos.z = pos1->z;
		speed = zdspeed + (GetRandomControl()&3);
		angle = GetRandomControl()<<1;
		fallspeed = (GetRandomControl()&3)-2;
	}
	else if (type == ZAPPDEBRIS)
	{
		pos.x = pos1->x;
		pos.y = pos1->y;
		pos.z = pos1->z;
		speed = (GetRandomControl()&7)+48;
		angle += (GetRandomControl()&0x1fff)-0x9000;
		fallspeed = -(GetRandomControl()&15)-16;
	}
	else if (type == DROPPER)
	{
		pos.x = pos1->x;
		pos.y = pos1->y;
		pos.z = pos1->z;
		speed = 0;
		fallspeed = (GetRandomControl()&3)+4;
	}
	else// if (type == DROPPERDEBRIS)
	{
		pos.x = pos1->x;
		pos.y = pos1->y;
		pos.z = pos1->z;
		speed = (GetRandomControl()&31)+32;
		angle = GetRandomControl()<<1;
		fallspeed = -(GetRandomControl()&31)-32;
	}

	fx_number = CreateEffect(room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->pos.x_pos = pos.x;
		fx->pos.y_pos = pos.y;
		fx->pos.z_pos = pos.z;
		fx->pos.y_rot = angle;
		fx->object_number = TONYFIREBALL;
		fx->speed = speed;
		fx->fallspeed = fallspeed;
		fx->flag1 = type;
		fx->flag2 = (GetRandomControl()&3)+1;
		if (type == ZAPPDEBRIS)
			fx->flag2 <<= 1;
		else if (type == ZAPP)
			fx->flag2 = 0;
	}
}

void ControlTonyFireBall(sint16 fx_number)
{
	FX_INFO	*fx;
	FLOOR_INFO	*floor;
	long		old_x, old_y, old_z, x;
	long		rnd,r,g,b;
	uchar 	radtab[7] = {	16,0,14,9,7,7,7	};
	sint16 room_number;

	fx = &effects[fx_number];

	old_x = fx->pos.x_pos;
	old_y = fx->pos.y_pos;
	old_z = fx->pos.z_pos;

	if (fx->flag1 == ROCKZAPPL || fx->flag1 == ROCKZAPPR)
	{
		fx->fallspeed += (fx->fallspeed>>3)+1;
		if (fx->fallspeed < -4096)
			fx->fallspeed = -4096;
		fx->pos.y_pos += fx->fallspeed;
		if (wibble&4)
			TriggerFireBallFlame(fx_number,fx->flag1,0,0,0);
	}
	else if (fx->flag1 == DROPPER)
	{
		fx->fallspeed+=2;
		fx->pos.y_pos += fx->fallspeed;
		if (wibble&4)
			TriggerFireBallFlame(fx_number,fx->flag1,0,0,0);
	}
	else
	{
		if (fx->flag1 != ZAPP)
		{
			if (fx->speed > 48)
				fx->speed--;
		}
		fx->fallspeed+=fx->flag2;
		if (fx->fallspeed > 512)
			fx->fallspeed = 512;
		fx->pos.y_pos += fx->fallspeed>>1;
		fx->pos.z_pos += (fx->speed * phd_cos(fx->pos.y_rot) >> W2V_SHIFT);
		fx->pos.x_pos += (fx->speed * phd_sin(fx->pos.y_rot) >> W2V_SHIFT);
		if (wibble&4)
			TriggerFireBallFlame(fx_number,fx->flag1,(old_x-fx->pos.x_pos)<<3,(old_y-fx->pos.y_pos)<<3,(old_z-fx->pos.z_pos)<<3);
	}

	room_number = fx->room_number;
	floor = GetFloor(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, &room_number);
	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->flag1 == ROCKZAPPL || fx->flag1 == ROCKZAPPR || fx->flag1 == ZAPP || fx->flag1 == DROPPER)
		{
			PHD_VECTOR	pos;

 			TriggerExplosionSparks(old_x, old_y, old_z,3,-2,0,fx->room_number);	// -2 = Set off a dynamic light controller.
			if (fx->flag1 == ROCKZAPPL || fx->flag1 == ROCKZAPPR)
			{
				for (x=0;x<2;x++)
 					TriggerExplosionSparks(old_x, old_y, old_z,3,-1,0, fx->room_number);
			}
			pos.x = old_x;
			pos.y = old_y;
			pos.z = old_z;
			if (fx->flag1 == ZAPP)
				r = 7;
			else
				r = 3;
			if (fx->flag1 == ZAPP)
				g = ZAPPDEBRIS;
			else if (fx->flag1 == DROPPER)
				g = DROPPERDEBRIS;
			else
				g = ROCKZAPPDEBRIS;

			for (x=0;x<r;x++)
				TriggerFireBall((ITEM_INFO *)NULL, g, &pos, fx->room_number, fx->pos.y_rot, 32+(x<<2));

			if (fx->flag1 == ROCKZAPPL || fx->flag1 == ROCKZAPPR)
			{
				room_number = lara_item->room_number;
				floor = GetFloor(lara_item->pos.x_pos, lara_item->pos.y_pos, lara_item->pos.z_pos, &room_number);
		 		pos.y = GetCeiling(floor, lara_item->pos.x_pos, lara_item->pos.y_pos, lara_item->pos.z_pos) + 256;
				pos.x = lara_item->pos.x_pos + (GetRandomControl()&1023) - 512;
				pos.z = lara_item->pos.z_pos + (GetRandomControl()&1023) - 512;
 				TriggerExplosionSparks(pos.x, pos.y, pos.z,3,-2,0,room_number);	// -2 = Set off a dynamic light controller.
				TriggerFireBall((ITEM_INFO *)NULL, DROPPER, &pos, room_number, 0, 0);
			}
		}
		KillEffect(fx_number);
		return;
	}

	if (room[room_number].flags & UNDERWATER)
	{
		KillEffect(fx_number);
		return;
	}

	if (!lara.burn)
	{
		if (ItemNearLara(&fx->pos, 200))
		{
			lara_item->hit_status = 1;
			KillEffect(fx_number);
#ifndef TONY_NOT_GOOD
			lara_item->hit_points -= 200;
			LaraBurn();
#endif
			return;
		}
	}

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

	if (radtab[fx->flag1])
	{
		rnd = GetRandomControl();
		r=31-((rnd>>4)&3);
		g=24-((rnd>>6)&3);
		b=rnd&7;
		TriggerDynamic(fx->pos.x_pos,fx->pos.y_pos,fx->pos.z_pos,radtab[fx->flag1],r,g,b);
	}
}

static void TonyBossFlipMap(void)
{
	// Flip to the alternative version of the map
	int i;
	ROOM_INFO *r, *flipped, temp;

	/* Stop all sound effects (may get restarted if still exist) */
	//mn_stop_ambient_samples();

	for (i=0, r=room; i<number_rooms; i++, r++)
	{
		if (r->flipped_room < 0)
			continue;

		flipped = &room[r->flipped_room];

		S_MemCpy(&temp, r, sizeof(ROOM_INFO));
		S_MemCpy(r, flipped, sizeof(ROOM_INFO));
		S_MemCpy(flipped, &temp, sizeof(ROOM_INFO));

		// Flip back a couple of things that don't want flipping
		r->flipped_room = flipped->flipped_room;
		flipped->flipped_room = -1;

		// Item flipping will be a nightmare!! Need to check out new floor heights and all sorts
		r->item_number = flipped->item_number;
		r->fx_number = flipped->fx_number;
//		AddRoomFlipItems( r );
	}
	// Toggle flipmap status
//	flip_status = !flip_status;
}

