
#ifdef PSX_VERSION
#include <stdio.h>
#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/specific.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 "text.h"
#include "laraelec.h"
#include "pickup.h"
#include "control.h"
#include "setup.h"

#define SIGN_BIT 0x80000000

void TriggerElectricSparks(GAME_VECTOR *pos, long shield);
void TriggerTribeBossHeadElectricity(ITEM_INFO *item, long copy);
void TriggerElectricBeam(ITEM_INFO *item, GAME_VECTOR *src, long copy);
void DrawTribeBossShield(ITEM_INFO *item);
void TribeBossDie(sint16 item_number);
void ExplodeTribeBoss(ITEM_INFO *item);
void RotateHeadXAngle(ITEM_INFO *item);
void TriggerLizardMan();
void TriggerSummonSmoke(long x, long y, long z);
sint16 FindLizardManItemNumber(sint16 room_number);

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

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

extern 	uchar	lara_meshes[];
extern	uchar	lara_last_points[];
extern	uchar	lara_line_counts[];
extern	short 	electricity_points[32][6];

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

#define	MAX_ELEC_POS	256
#define	MAX_ELEC_POS2	MAX_ELEC_POS+(MAX_ELEC_POS>>1)

enum	{	ATTACK_HEAD, ATTACK_HAND1, ATTACK_HAND2	};

// Tribe boss.

enum tribeboss_anims {TRIBEBOSS_WAIT, TRIBEBOSS_ATTACK_HEAD, TRIBEBOSS_ATTACK_HAND, TRIBEBOSS_DEATH };

#define TRIBEBOSS_TURN 		(ONE_DEGREE*3)
#define TRIBEBOSS_DIE_ANIM 	3
#define TRIBEBOSS_HAND_R 	14
#define MAX_HEAD_ATTACKS		4

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

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

void InitialiseTribeBoss(sint16 item_number);
void TribeBossControl(sint16 item_number);
void S_DrawTribeBoss(ITEM_INFO *item);
void FindClosestShieldPoint(long x, long y, long z, ITEM_INFO *item);

void *func[] __attribute__((section(".header"))) = {
	InitialiseTribeBoss,
	TribeBossControl,
	S_DrawTribeBoss,
	FindClosestShieldPoint,
};

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

static long lizman_summon_coords[2][4]	=	{		// X,      Y,      Z,    Angle
										{	0xf200, -0x1500, 0xf600, 0xe000 	},
										{	0xf200, -0x1500, 0xce00, 0xa000	}
									};

static BITE_INFO tribeboss_hit[6] = 	{	{120,68,136, 8},
									{128,-64,136, 8},
									{8,-120,136, 8},
									{-128,-64,136, 8},
									{-124,64,136, 8},
									{8,32,400, 8}
								};
static uchar shield_active;

PHD_VECTOR	TrigDynamics[3];	// 0 - Electric from head
							// 1 - Electric beam hitting wall.
							// 2 - Boss firing electric beam.

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	TribeBossShield[40];	// x,y,z,rgb.
static EXPLOSION_RING	ExpRings[7];

static uchar	turned = 0;

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

#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);
				b = (GetRandomDraw()&63)+224;
				g = (b>>2)+(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);
			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 int LaraOnLOS(GAME_VECTOR *start, GAME_VECTOR *target)
{
	int j, failure;
	sint32 x, y, z, dx, dy, dz, distance;
	sint32 direction;
	sint16 *bounds, *zextent, *xextent;
	ITEM_INFO *item;

	dx = target->x - start->x;
	dy = target->y - start->y;
	dz = target->z - start->z;

	item = lara_item;

	/* Get bounds of object */
	bounds = GetBoundsAccurate(item);

	/* Need to allow for rotation of object (assume aligned to axis) */
	direction = (uint16)(item->pos.y_rot+0x2000)/0x4000;
	if (direction & 1)
	{
		zextent = &bounds[0];
		xextent = &bounds[4];
	}
	else
	{
		zextent = &bounds[4];
		xextent = &bounds[0];
	}

	/* If dz is longest axis, then check x axis faces, else vice versa */
	failure = 0;
	if (ABS(dz) > ABS(dx))
	{
		/* Check z min */
		distance = item->pos.z_pos + zextent[0] - start->z;
		for (j=0; j<2; j++) // cunning loop to do this check twice (once for each distance)
		{
			if ((distance & SIGN_BIT) == (dz & SIGN_BIT))
			{
				y = dy * distance / dz;
				if (y > item->pos.y_pos + bounds[2] - start->y && y < item->pos.y_pos + bounds[3] - start->y)
				{
					x = dx * distance / dz;
					if (x < item->pos.x_pos + xextent[0] - start->x)
						failure |= 0x1;
					else if (x > item->pos.x_pos + xextent[1] - start->x)
						failure |= 0x2;
					else
						return (1);
				}
			}

			/* Check z max */
			distance = item->pos.z_pos + zextent[1] - start->z;
		}

		/* If failed on both a greater than and less than count, actually will have passed through object */
		if (failure == 0x3)
			return (1);
	}
	else
	{
		/* Check x min */
		distance = item->pos.x_pos + xextent[0] - start->x;
		for (j=0; j<2; j++) // cunning loop to do this check twice (once for each distance)
		{
			if ((distance & SIGN_BIT) == (dx & SIGN_BIT))
			{
				y = dy * distance / dx;
				if (y > item->pos.y_pos + bounds[2] - start->y && y < item->pos.y_pos + bounds[3] - start->y)
				{
					z = dz * distance / dx;
					if (z < item->pos.z_pos + zextent[0] - start->z)
						failure |= 0x1;
					else if (z > item->pos.z_pos + zextent[1] - start->z)
						failure |= 0x2;
					else
						return (1);	/* Hit object! */
				}
			}

			/* Check x max */
			distance = item->pos.x_pos + xextent[1] - start->x;
		}

		/* If failed on both left/right then passed through object, so a hit really */
		if (failure == 0x3)
			return (1);
	}

	/* Nothing smashable has been hit */
	return (0);
}


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

void TribeBossControl(sint16 item_number)
{
//	ITEM_INFO *item, *enemy;
	ITEM_INFO *item;
	CREATURE_INFO *tribeboss;
	sint16 angle, head, tilt,lp,dynsize;
	uint16 ty;
	AI_INFO info;
	sint16	tyrot;

	for (lp=0;lp<3;lp++)
	{
		if (TrigDynamics[lp].x != 0)
		{
			if (lp == 0)
				TriggerDynamic(TrigDynamics[0].x, TrigDynamics[0].y, TrigDynamics[0].z,(GetRandomControl()&3)+8, 0, (GetRandomControl()&7)+8, (GetRandomControl()&7)+16);
			else if (lp == 1)
			{
				if (bossdata.attack_type == ATTACK_HEAD)
					TriggerDynamic(TrigDynamics[1].x, TrigDynamics[1].y, TrigDynamics[1].z,(GetRandomControl()&7)+8, 0, (GetRandomControl()&7)+8, (GetRandomControl()&7)+16);
				else
					TriggerDynamic(TrigDynamics[1].x, TrigDynamics[1].y, TrigDynamics[1].z,(GetRandomControl()&7)+8, 0, (GetRandomControl()&7)+16, (GetRandomControl()&7)+8);
			}
			else
			{
				if (bossdata.attack_count)
				{
					dynsize = (128-bossdata.attack_count)>>1;
					if (dynsize > 31)
						dynsize = 31;
					if (dynsize > 0)
					{
						if (bossdata.attack_type == ATTACK_HEAD)
							TriggerDynamic(TrigDynamics[2].x, TrigDynamics[2].y, TrigDynamics[2].z, dynsize, dynsize>>2, (GetRandomControl()&7)+16, (GetRandomControl()&7)+24);
						else
							TriggerDynamic(TrigDynamics[2].x, TrigDynamics[2].y, TrigDynamics[2].z, dynsize, dynsize>>2, (GetRandomControl()&7)+24, (GetRandomControl()&7)+16);
					}
				}
			}
		}
	}

	if (!CreatureActive(item_number))
		return;

	TribeBossItem = item = &items[item_number];
	tribeboss = (CREATURE_INFO *)item->data;
	head = angle = tilt = 0;

	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != TRIBEBOSS_DEATH)
		{
			item->anim_number = objects[item->object_number].anim_index + TRIBEBOSS_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = TRIBEBOSS_DEATH;
			bossdata.death_count = 1;
		}

		if (item->frame_number - anims[item->anim_number].frame_base > 119)
		{
			item->frame_number = anims[item->anim_number].frame_base + 120;
			item->mesh_bits = 0;	// Don't draw any of the boss while I'm killing it.
			bossdata.death_count = -1;
			if (bossdata.explode_count == 0)
			{
				bossdata.ring_count = 0;
				SoundEffect( 106, &item->pos,NULL);

				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)
			{
				TribeBossDie(item_number);
				bossdata.dead = 1;
			}
			else
				ExplodeTribeBoss(item);
			return;
		}

		item->pos.z_rot = (GetRandomControl()%bossdata.death_count)-(bossdata.death_count>>1);
		if (bossdata.death_count < 2048)
			bossdata.death_count += 32;
	}
	else
	{
		CreatureAIInfo(item, &info);

		if (item->hit_status)
			SoundEffect( 361, &item->pos,0);

		tyrot = item->pos.y_rot;

		if (!bossdata.attack_flag)
		{
			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)) <  (2048*2048))
				bossdata.attack_flag = 1;
		}

		tribeboss->target.x = lara_item->pos.x_pos;
		tribeboss->target.z = lara_item->pos.z_pos;
		if (lizard_man_active && item->current_anim_state == TRIBEBOSS_WAIT)	// No lizard-man summoned ?
		{
		 	ty = (uint16) item->pos.y_rot;
			if (abs(0xc000-ty) > ONE_DEGREE)
		 		item->pos.y_rot += (0xc000 - ty) >> 3;
			else
		 		item->pos.y_rot = 0xc000;
		}
		else
			angle = CreatureTurn(item, tribeboss->maximum_turn);

		RotateHeadXAngle(item);

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

		switch (item->current_anim_state)
		{
			case TRIBEBOSS_WAIT:	// Waiting.
				bossdata.attack_count = 0;

				if (	item->goal_anim_state != TRIBEBOSS_ATTACK_HEAD &&
					item->goal_anim_state != TRIBEBOSS_ATTACK_HAND)
					TribeBossShieldOn = 1;

				if (lizard_man_active)
					CreatureJoint(item, 1, head);	// Rotate head to watch Lara.
				else
					CreatureJoint(item, 1, 0);	// Rotate head to point forward.

				if (bossdata.attack_flag && !lizard_man_active)
					tribeboss->maximum_turn = TRIBEBOSS_TURN;
				else
					tribeboss->maximum_turn = 0;

				if (item->goal_anim_state != TRIBEBOSS_ATTACK_HEAD && info.angle > -128 && info.angle < 128
					&& lara_item->hit_points > 0 && bossdata.attack_head_count < MAX_HEAD_ATTACKS && !lizard_man_active
					&& !shield_active)
				{
					PHD_VECTOR	pos;

					pos.x = pos.y = pos.z = 0;
					GetJointAbsPosition(lara_item,&pos,0);	// Get position of Lara's hips.
					bossdata.BeamTarget.x = pos.x;
					bossdata.BeamTarget.y = pos.y;
					bossdata.BeamTarget.z = pos.z;
					item->goal_anim_state = TRIBEBOSS_ATTACK_HEAD;
			   		tribeboss->maximum_turn = 0;
					TribeBossShieldOn = 0;
					bossdata.attack_head_count++;
					break;
				}
				else if (item->goal_anim_state != TRIBEBOSS_ATTACK_HAND && bossdata.attack_head_count >= MAX_HEAD_ATTACKS
					&& lara_item->hit_points > 0)
				{
			   		tribeboss->maximum_turn = 0;

					if (bossdata.attack_type == ATTACK_HEAD)
					{
						long dxz1,dxz2;

						dxz1 = SQUARE(lara_item->pos.x_pos - lizman_summon_coords[0][0]);
						dxz1 += SQUARE(lara_item->pos.z_pos - lizman_summon_coords[0][2]);
						dxz2 = SQUARE(lara_item->pos.x_pos - lizman_summon_coords[1][0]);
						dxz2 += SQUARE(lara_item->pos.z_pos - lizman_summon_coords[1][2]);

						// Get furthest lizard man summon point from Lara.

						if (dxz1 > dxz2)
							bossdata.attack_type = ATTACK_HAND1;
						else
							bossdata.attack_type = ATTACK_HAND2;
					}

					ty = (uint16) item->pos.y_rot;
					if (abs(lizman_summon_coords[bossdata.attack_type-1][3] - ty) < ONE_DEGREE)
					{
						item->pos.y_rot = lizman_summon_coords[bossdata.attack_type-1][3];
						if (!shield_active)
						{
							item->goal_anim_state = TRIBEBOSS_ATTACK_HAND;
							bossdata.BeamTarget.x = lizman_summon_coords[bossdata.attack_type-1][0];
							bossdata.BeamTarget.y = lizman_summon_coords[bossdata.attack_type-1][1];
							bossdata.BeamTarget.z = lizman_summon_coords[bossdata.attack_type-1][2];
			   				tribeboss->maximum_turn = 0;
							bossdata.attack_head_count = 0;
							TribeBossShieldOn = 0;
							break;
						}
					}
					else
						item->pos.y_rot += (lizman_summon_coords[bossdata.attack_type-1][3] - ty) >> 4;
				}
//				else if (item->goal_anim_state != TRIBEBOSS_ATTACK_HAND && lara_item->hit_points > 0 && bossdata.attack_head_count >= MAX_HEAD_ATTACKS)
//				{
//					item->goal_anim_state = TRIBEBOSS_ATTACK_HAND;
//					bossdata.BeamTarget.x = lara_item->pos.x_pos;
//					bossdata.BeamTarget.y = lara_item->pos.y_pos-512;
//					bossdata.BeamTarget.z = lara_item->pos.z_pos;
//			   		tribeboss->maximum_turn = 0;
//					bossdata.attack_head_count = 0;
//					break;
//				}

				break;

			case TRIBEBOSS_ATTACK_HEAD:	// Attack from head - electric beam.
//				if (item->frame_number - anims[item->anim_number].frame_base < 20)
				tribeboss->maximum_turn = 0;
				bossdata.attack_count += 3;
				bossdata.attack_type = ATTACK_HEAD;
				CreatureJoint(item, 1, 0);	// Rotate head to point forward.
				break;

			case TRIBEBOSS_ATTACK_HAND:	// Attack from head - electric beam.
				tribeboss->maximum_turn = 0;
				if (bossdata.attack_count < 64)
					bossdata.attack_count += 2;
				else
					bossdata.attack_count += 3;
				CreatureJoint(item, 1, 0);	// Rotate head to point forward.
				break;

			default:
				break;
		}
	}

	if (bossdata.attack_count && bossdata.attack_type == ATTACK_HEAD)
	{
		if (bossdata.attack_count < 64)
			tribeboss_hit[5].z = 136 + (bossdata.attack_count<<2);	// Extend cone of electricity at front of head.
	}

//	item->pos.y_rot = 0x4000;
	tribeboss->joint_rotation[0] += ONE_DEGREE*7;	// Rotate icon.

	/* Actually do animation allowing for collisions */
	CreatureAnimation(item_number, angle, 0);

	if (tyrot != item->pos.y_rot && turned == 0)
	{
		turned = 1;
		SoundEffect( 362, &item->pos,PITCH_SHIFT|0x800000);
	}
	else if (tyrot == item->pos.y_rot)
		turned = 0;

}

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

	UpdateElectricityPoints();

    	TrigDynamics[0].x = 0;
    	TrigDynamics[1].x = 0;
    	TrigDynamics[2].x = 0;

	if (!bossdata.explode_count)
	{
		TriggerTribeBossHeadElectricity(item,0);
		TriggerTribeBossHeadElectricity(item,1);
	}
	else
		DrawExplosionRings();

	DrawTribeBossShield(item);
}

void TriggerElectricSparks(GAME_VECTOR *pos, long shield)
{
	SPARKS	*sptr;
	long		dx,dz;

	dx = lara_item->pos.x_pos - pos->x;
	dz = lara_item->pos.z_pos - pos->z;

	if (dx < -0x5000 || dx > 0x5000 || dz < -0x5000 || dz > 0x5000)
		return;

	TrigDynamics[1].x = pos->x;
	TrigDynamics[1].y = pos->y;
	TrigDynamics[1].z = pos->z;

	sptr = &spark[GetFreeSpark()];

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

	if (shield)
	{
		sptr->dR = 255;
		sptr->dG = 64 + (GetRandomControl()&127);
		sptr->dB = 0;
	}
	else if (bossdata.attack_type == ATTACK_HEAD)
	{
		sptr->dR = 0;
		sptr->dG = (GetRandomControl()&127)+64;
		sptr->dB = (sptr->dG>>1)+128;
	}
	else
	{
		sptr->dR = 0;
		sptr->dB = (GetRandomControl()&127)+64;
		sptr->dG = (sptr->dB>>1)+128;
	}


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

	sptr->x = pos->x + (GetRandomControl()&31)-16;
	sptr->y = pos->y + (GetRandomControl()&31)-16;
	sptr->z = pos->z + (GetRandomControl()&31)-16;
	sptr->Xvel = ((GetRandomControl()&511)-256)<<2;
	sptr->Yvel = ((GetRandomControl()&511)-256)<<1;
	sptr->Zvel = ((GetRandomControl()&511)-256)<<2;
	if (shield)
	{
		sptr->Xvel >>= 1;
		sptr->Yvel >>= 1;
		sptr->Zvel >>= 1;
	}

	sptr->Friction = 4;//|(4<<4);
	sptr->Flags = SP_SCALE;
	sptr->Scalar = 3;
	sptr->Width = sptr->sWidth = (GetRandomControl()&1)+1;
	sptr->dWidth = (GetRandomControl()&3)+4;
	sptr->Height = sptr->sHeight = (GetRandomControl()&1)+1;
	sptr->dHeight = (GetRandomControl()&3)+4;
	sptr->Gravity = 15;
	sptr->MaxYvel = 0;
}


#ifdef PSX_VERSION

void TriggerTribeBossHeadElectricity(ITEM_INFO *item, long copy)
{
	PHD_VECTOR	pos1,pos2;
	LINE_G2 		*lineg2;
	BITE_INFO		*bite1,*bite2;
	GAME_VECTOR	src;
	short		*scrxy;	// X,Y
	long			*scrz;	// Z
	short		*distances;	// distances for rgbs.
	short		*TempMesh;
	short		*leccy;
	long			dx,dy,dz;
	long			wx,wy,wz,tx,ty,tz,xv,yv,zv;
	long			lp,lp1,cnt;
	long			shadeval;

	dx = lara_item->pos.x_pos - item->pos.x_pos;
	dz = lara_item->pos.z_pos - item->pos.z_pos;
	if (dx < -0x4800 || dx > 0x4800 || dz < -0x4800 || dz > 0x4800)
		return;

	shadeval = 64 + ((rcossin_tbl[wibble<<5]>>6)>>1);

	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.
	distances = (short *) 0x1f800200;	// Set pointer to scratch pad.
	TempMesh = (short *) 0x1f800300;	// Set pointer to scratch pad.

	leccy = &electricity_points[0][0];

	for (lp=0;lp<4;lp++)
	{
		bite1 = &tribeboss_hit[lp];
		bite2 = &tribeboss_hit[lp+1];

		pos1.x = bite1->x;             // Copy Offsets from mesh
		pos1.y = bite1->y;             // Pivot
		pos1.z = bite1->z;
		GetJointAbsPosition(item, &pos1, bite1->mesh_num );

		pos2.x = bite2->x;             // Copy Offsets from mesh
		pos2.y = bite2->y;             // Pivot
		pos2.z = bite2->z;
		GetJointAbsPosition(item, &pos2, bite2->mesh_num );

		if (lp==2)
		{
			TrigDynamics[0].x = pos1.x;
			TrigDynamics[0].y = pos1.y;
			TrigDynamics[0].z = pos1.z;
		}

		pos1.x -= item->pos.x_pos;
		pos1.y -= item->pos.y_pos;
		pos1.z -= item->pos.z_pos;
		pos2.x -= item->pos.x_pos;
		pos2.y -= item->pos.y_pos;
		pos2.z -= item->pos.z_pos;

		wx = pos1.x;
		wy = pos1.y;
		wz = pos1.z;
		dx = (pos2.x - pos1.x) >> 2;
		dy = (pos2.y - pos1.y) >> 2;
		dz = (pos2.z - pos1.z) >> 2;

		for (lp1=0;lp1<5;lp1++)
		{
			if (lp1 == 4 && lp != 3)
				break;
			else if (lp1 == 4 && lp == 3)	// Last point?
			{
				TempMesh[0] = pos2.x;
				TempMesh[1] = pos2.y;
				TempMesh[2] = pos2.z;
			}
			else
			{
				TempMesh[0] = wx;
				TempMesh[1] = wy;
				TempMesh[2] = wz;
			}

			if (lp1 != 0 && lp1 != 4)
			{
				tx = *leccy++;
				ty = *leccy++;
				tz = *leccy++;

				if (copy)
				{
					TempMesh[0] -= tx >> 3;
					TempMesh[1] -= ty >> 3;
					TempMesh[2] -= tz >> 3;
				}
				else
				{
					TempMesh[0] += tx >> 3;
					TempMesh[1] += ty >> 3;
					TempMesh[2] += tz >> 3;
				}
				leccy+=3;

				xv = abs(tx);
				yv = abs(ty);
				zv = abs(tz);
				if (yv > xv)
					xv = yv;
				if (zv > xv)
					xv = zv;

				*distances++ = xv;
			}
			else
				*distances++ = 0;

			gte_ldv0(TempMesh);
			gte_rtps();
			wx += dx;
			wy += dy;
			wz += dz;
			gte_stsxy(scrxy);
			gte_stsz(scrz);
			scrxy+=2;
			scrz++;
		}
	}

	leccy = &electricity_points[16][0];
	cnt = 0;

	if (bossdata.attack_count && !bossdata.death_count && bossdata.attack_type == ATTACK_HEAD)
	{
		for (lp=0;lp<5;lp++)
		{
			bite1 = &tribeboss_hit[lp];
			bite2 = &tribeboss_hit[5];

			pos1.x = bite1->x;             // Copy Offsets from mesh
			pos1.y = bite1->y;             // Pivot
			pos1.z = bite1->z;
			GetJointAbsPosition(item, &pos1, bite1->mesh_num );

			pos2.x = bite2->x;             // Copy Offsets from mesh
			pos2.y = bite2->y;             // Pivot
			pos2.z = bite2->z;
			GetJointAbsPosition(item, &pos2, bite2->mesh_num );

			pos1.x -= item->pos.x_pos;
			pos1.y -= item->pos.y_pos;
			pos1.z -= item->pos.z_pos;
			pos2.x -= item->pos.x_pos;
			pos2.y -= item->pos.y_pos;
			pos2.z -= item->pos.z_pos;

			wx = pos1.x;
			wy = pos1.y;
			wz = pos1.z;
			dx = (pos2.x - pos1.x) >> 2;
			dy = (pos2.y - pos1.y) >> 2;
			dz = (pos2.z - pos1.z) >> 2;

			for (lp1=0;lp1<5;lp1++)
			{
				if (lp1 == 4)	// Last point?
				{
					TempMesh[0] = pos2.x;
					TempMesh[1] = pos2.y;
					TempMesh[2] = pos2.z;
					if (lp == 4)
					{
						src.x = pos2.x + item->pos.x_pos;	// Set up source of death beam.
						src.y = pos2.y + item->pos.y_pos;
						src.z = pos2.z + item->pos.z_pos;

						if (bossdata.attack_count >= 64 && bossdata.attack_count <= 128)
						{
							TrigDynamics[2].x = src.x;
							TrigDynamics[2].y = src.y;
							TrigDynamics[2].z = src.z;
						}

					}
				}
				else
				{
					TempMesh[0] = wx;
					TempMesh[1] = wy;
					TempMesh[2] = wz;
				}

				if (lp1 != 0 && lp1 != 4)
				{
					cnt++;
					if (cnt > 15)
					{
						leccy = &electricity_points[0][0];
						cnt = 0;
					}
					if (copy)
					{
						TempMesh[0] -= (tx = *leccy++) >> 4;
						TempMesh[1] -= (ty = *leccy++) >> 4;
					}
					else
					{
						TempMesh[0] += (tx = *leccy++) >> 4;
						TempMesh[1] += (ty = *leccy++) >> 4;
					}

					leccy += 4;

					xv = abs(tx);
					yv = abs(ty);
					if (yv > xv)
						xv = yv;
					*distances++ = xv;
				}
				else
					*distances++ = 0;

				gte_ldv0(TempMesh);
				gte_rtps();
				wx += dx;
				wy += dy;
				wz += dz;
				gte_stsxy(scrxy);
				gte_stsz(scrz);
				scrxy+=2;
				scrz++;
			}
		}
	}

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

	for (lp=0;lp<16;lp++)        	// Electricity on head.
	{
		long	x1,x2,y1,y2,z1,rgb,rgb1;

		if ((char *)db.polyptr >= db.polybuf_limit)
		{
			mPopMatrix();
			return;
		}

		x1 = *scrxy++;
		y1 = *scrxy++;
		x2 = *scrxy;
		y2 = *(scrxy+1);
		z1 = (*scrz++)>>3;
		rgb = *distances++;
		rgb1 = *distances;

		if (z1<32 || z1 > 0xa00)
			continue;

		lineg2 = (LINE_G2 *)db.polyptr;
		setlen(lineg2, 4);
		setcode(lineg2, 0x52);

		if (rgb > 255)
		{
			rgb = 511-rgb;
			if (rgb < 0)
				rgb = 0;
		}

		if (rgb1 > 255)
		{
			rgb1 = 511-rgb1;
			if (rgb1 < 0)
				rgb1 = 0;
		}

		if (copy)
		{
			rgb >>= 1;
			rgb1 >>= 1;
		}

		rgb = (rgb * shadeval)>>6;
		rgb1 = (rgb1 * shadeval)>>6;

		setRGB0(lineg2,0,rgb,rgb);
		setRGB1(lineg2,0,rgb1,rgb1);
		setXY2(lineg2,x1,y1,x2,y2);
		MyaddPrim(db.ot, z1, lineg2);
		db.polyptr += sizeof(LINE_G2);

		if ((char *)db.polyptr >= db.polybuf_limit)
		{
			mPopMatrix();
			return;
		}

		setDrawTPage((DR_TPAGE *)db.polyptr, 0, 1, getTPage(0, 1, 0, 0));
		MyaddPrim( db.ot,z1,(DR_TPAGE *)db.polyptr);
		db.polyptr += sizeof(DR_TPAGE);
	}

	if (bossdata.attack_count && !bossdata.death_count && bossdata.attack_type == ATTACK_HEAD)
	{
		for (lp=0;lp<5;lp++)        	// Electricity from 5 head points to initial target point.
		{
			scrxy+=2;
			scrz++;
			distances++;

			for (lp1=0;lp1<4;lp1++)
			{
				long	x1,x2,y1,y2,z1,rgb,rgb1;

				if ((char *)db.polyptr >= db.polybuf_limit)
				{
					mPopMatrix();
					return;
				}

				x1 = *scrxy++;
				y1 = *scrxy++;
				x2 = *scrxy;
				y2 = *(scrxy+1);
				z1 = (*scrz++)>>3;
				rgb = *distances++;
				rgb1 = *distances;

				if (z1<32 || z1 > 0xa00)
					continue;

				lineg2 = (LINE_G2 *)db.polyptr;
				setlen(lineg2, 4);
				setcode(lineg2, 0x52);

				if (rgb > 255)
				{
					rgb = 511-rgb;
					if (rgb < 0)
						rgb = 0;
				}

				if (rgb1 > 255)
				{
					rgb1 = 511-rgb1;
					if (rgb1 < 0)
						rgb1 = 0;
				}

				if (copy)
				{
					rgb >>= 1;
					rgb1 >>= 1;
				}

				if (bossdata.attack_count < 64)
				{
					rgb = (rgb * bossdata.attack_count)>>6;
					rgb1 = (rgb1 * bossdata.attack_count)>>6;
				}

				setRGB0(lineg2,0,rgb,rgb);
				setRGB1(lineg2,0,rgb1,rgb1);
				setXY2(lineg2,x1,y1,x2,y2);
				MyaddPrim(db.ot, z1, lineg2);
				db.polyptr += sizeof(LINE_G2);

				if ((char *)db.polyptr >= db.polybuf_limit)
				{
					mPopMatrix();
					return;
				}

				setDrawTPage((DR_TPAGE *)db.polyptr, 0, 1, getTPage(0, 1, 0, 0));
				MyaddPrim( db.ot,z1,(DR_TPAGE *)db.polyptr);
				db.polyptr += sizeof(DR_TPAGE);
			}
		}
	}


	if (bossdata.attack_count && !bossdata.death_count && (bossdata.attack_type == ATTACK_HAND1 || bossdata.attack_type == ATTACK_HAND2))
	{
		pos1.x = 0;
		pos1.y = 0;
		pos1.z = 0;
		GetJointAbsPosition(item, &pos1, TRIBEBOSS_HAND_R );
		src.x = pos1.x;	// Set up source of death beam.
		src.y = pos1.y;
		src.z = pos1.z;
		if (bossdata.attack_count >= 64 && bossdata.attack_count <= 128)
		{
			TrigDynamics[2].x = src.x;
			TrigDynamics[2].y = src.y;
			TrigDynamics[2].z = src.z;
		}
		if (bossdata.attack_count >= 64 && bossdata.attack_count <= 96)
		{
			if (bossdata.attack_count > 90 && !lizard_man_active)
				TriggerLizardMan();
			TriggerElectricBeam(item,&src,copy);
			for (lp=0;lp<3;lp++)
				TriggerSummonSmoke(bossdata.BeamTarget.x, bossdata.BeamTarget.y, bossdata.BeamTarget.z);
		}
	}
	else if (bossdata.attack_count > 64 && !bossdata.death_count && bossdata.attack_type == ATTACK_HEAD)
		TriggerElectricBeam(item,&src,copy);

	mPopMatrix();
}



void TriggerElectricBeam(ITEM_INFO *item, GAME_VECTOR *src, long copy)
{
	CREATURE_INFO *tribeboss;
	GAME_VECTOR	target;
	POLY_G4	*polyg4;
	short 	angle;
	long		lp;
	long		segments,dx,dy,dz,longest;
	long 	addx,addz,addy1,addy2;
	long 	wx,wy,wz;
	short	*scrxy;	// X,Y
	long		*scrz;	// Z
	short	*TempMesh;
	short	*leccy;

	src->room_number = item->room_number;

	tribeboss = (CREATURE_INFO *)item->data;

	angle = item->pos.y_rot>>4;
	angle &= 4095;

	dx = bossdata.BeamTarget.x - src->x;
	dy = bossdata.BeamTarget.y - src->y;
	dz = bossdata.BeamTarget.z - src->z;

	longest = abs(dx);
	if (abs(dy)>longest)
		longest = abs(dy);
	if (abs(dz)>longest)
		longest = abs(dz);

	if (longest < 0x5000)
	{
		longest = 0x5000 / longest;
		dx += dx * longest;
		dy += dy * longest;
		dz += dz * longest;
	}

	target.x = src->x + dx;
	target.y = src->y + dy;
	target.z = src->z + dz;

	LOS(src,&target);	// Get target hit point.... assumes it will hit a wall!
	if (!lara.electric && !copy && bossdata.attack_type == ATTACK_HEAD)
	{
		if (LaraOnLOS(src, &target))
		{
			lara_item->hit_points = 0;
			lara.electric = 1;
		}
	}

	TriggerElectricSparks(&target,0);

	dx = abs(target.x - src->x);
	dz = abs(target.z - src->z);
	if (dx<dz)
		segments = dz>>8; 	// Beam in half block chunks.
	else
		segments = dx>>8;	// Beam in half block chunks.

	if (segments < 8)
		segments = 8;
	else if (segments > 24)
		segments = 24;

	dx = (target.x - src->x) / (segments);
	dy = (target.y - src->y) / (segments);
	dz = (target.z - src->z) / (segments);
	wx = src->x - item->pos.x_pos;
	wy = src->y - item->pos.y_pos;
	wz = src->z - item->pos.z_pos;

	angle += 0x400;
	angle &= 4095;

	if (bossdata.attack_type == ATTACK_HAND1 || bossdata.attack_type == ATTACK_HAND2)
	{
		addx = ((short)rcossin_tbl[angle<<1])>>8;
		addz = ((short)rcossin_tbl[(angle<<1)+1])>>8;
	}
	else
	{
		addx = ((short)rcossin_tbl[angle<<1])>>7;
		addz = ((short)rcossin_tbl[(angle<<1)+1])>>7;
	}
	addy1 = addy2 = 0;

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

// Do first point.

	TempMesh[0] = wx;
	TempMesh[1] = wy;
	TempMesh[2] = wz;

	gte_ldv0(TempMesh);
	gte_rtps();
	gte_stsxy(scrxy);
	gte_stsz(scrz);
	*(scrxy+2) = *scrxy;
	*(scrxy+3) = *(scrxy+1);
	*(scrz+1) = *scrz;
	scrxy+=4;
	scrz+=2;

	leccy = &electricity_points[copy<<2][0];

	for (lp=0;lp<segments-1;lp++)
	{
		long	rx,ry,rz;

		if (copy)
		{
			rx = -(*leccy++);
			ry = -(*leccy++)>>1;
			rz = -(*leccy++);
		}
		else
		{
			rx = (*leccy++);
			ry = (*leccy++)>>1;
			rz = (*leccy++);
		}
		leccy+=3;

		wx += dx;
		wy += dy;
		wz += dz;

		addy1 += (GetRandomControl()&31)-16;
		addy2 += (GetRandomControl()&31)-16;
		if (addy1 < -256)
			addy1 = -192;
		else if (addy1 > 256)
			addy1 = 192;
		if (addy2 < -256)
			addy2 = -192;
		else if (addy2 > 256)
			addy2 = 192;

		TempMesh[0] = wx+addx+rx;
		TempMesh[1] = wy+addy1+ry;
		TempMesh[2] = wz+addz+rz;

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

		TempMesh[0] = wx-addx+rx;
		TempMesh[1] = wy+addy2+ry;
		TempMesh[2] = wz-addz+rz;

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


// Do last point.

	TempMesh[0] = target.x-item->pos.x_pos;
	TempMesh[1] = target.y-item->pos.y_pos;
	TempMesh[2] = target.z-item->pos.z_pos;

	gte_ldv0(TempMesh);
	gte_rtps();
	gte_stsxy(scrxy);
	gte_stsz(scrz);
	*(scrxy+2) = *scrxy;
	*(scrxy+3) = *(scrxy+1);
	*(scrz+1) = *scrz;

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

	{
	 	long	x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,rgb1,rgb2,rgb3,rgb4;

		x1 = *scrxy++;
		y1 = *scrxy++;
		x2 = *scrxy++;
		y2 = *scrxy++;
		z1 = *scrz++;
		z2 = *scrz++;
		rgb1 = rgb2 = 0;

		for (lp=0;lp<segments;lp++)
		{
			if ((char *)db.polyptr >= db.polybuf_limit)
				return;

			if (lp!=segments-1)
			{
				rgb3 = (GetRandomControl()&255)>>copy;
				rgb4 = (GetRandomControl()&255)>>copy;
			}
			else
			{
				rgb3 = rgb4 = 0;
			}

			x3 = *scrxy++;
			y3 = *scrxy++;
			x4 = *scrxy++;
			y4 = *scrxy++;
			z3 = *scrz++;
			z4 = *scrz++;

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

			if (z1<32 || z1 > 0xa00)
				continue;

			polyg4 = (POLY_G4 *)db.polyptr;
			setlen(polyg4, 8);
			setcode(polyg4, 0x3a);

			if (bossdata.attack_type == ATTACK_HEAD)
			{
				setRGB0(polyg4,(rgb1&0xc0)>>1,rgb1,rgb1);
				setRGB1(polyg4,(rgb2&0xc0)>>1,rgb2,rgb2);
				setRGB2(polyg4,(rgb3&0xc0)>>1,rgb3,rgb3);
				setRGB3(polyg4,(rgb4&0xc0)>>1,rgb4,rgb4);
			}
			else
			{
				setRGB0(polyg4,(rgb1&0xc0)>>1,rgb1,(rgb1&0xc0)>>1);
				setRGB1(polyg4,(rgb2&0xc0)>>1,rgb2,(rgb2&0xc0)>>1);
				setRGB2(polyg4,(rgb3&0xc0)>>1,rgb3,(rgb3&0xc0)>>1);
				setRGB3(polyg4,(rgb4&0xc0)>>1,rgb4,(rgb4&0xc0)>>1);
			}
			setXY4(polyg4,x1,y1,x2,y2,x3,y3,x4,y4);
			MyaddPrim(db.ot, z1, polyg4);
			db.polyptr += sizeof(POLY_G4);

			if ((char *)db.polyptr >= db.polybuf_limit)
				return;

			setDrawTPage((DR_TPAGE *)db.polyptr, 0, 1, getTPage(0, 1, 0, 0));
			MyaddPrim( db.ot,z1,(DR_TPAGE *)db.polyptr);
			db.polyptr += sizeof(DR_TPAGE);

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

#endif

void TribeBossDie(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 InitialiseTribeBoss(sint16 item_number)
{
	SHIELD_POINTS	*shptr;
	long		lp,lp1,angle,y;

	bossdata.LizmanItem = FindLizardManItemNumber(items[item_number].room_number);
	bossdata.LizmanRoom = items[bossdata.LizmanItem].room_number;

	for (lp=0;lp<3;lp++)
		TrigDynamics[lp].x = 0;	// Clear all Tribe Boss dynamic lights.

	bossdata.attack_count = bossdata.attack_flag = bossdata.death_count = bossdata.attack_head_count =
	lizard_man_active = TribeBossShieldOn = shield_active = bossdata.explode_count = bossdata.ring_count =
	bossdata.dead = bossdata.dropped_icon = 0;

// Initialise shield coordinates.

	shptr = &TribeBossShield[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

void DrawTribeBossShield(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.

	shield_active = 0;
	shptr = &TribeBossShield[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;

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

		shptr++;

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

	shptr = &TribeBossShield[0];
	shptr2 = &TribeBossShield[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

void FindClosestShieldPoint(long x, long y, long z, ITEM_INFO *item)
{
	SHIELD_POINTS	*shptr;
	GAME_VECTOR	pos;
	long		affected[5] = { 	0,-1,1,-8,8	};
	long		closestdist,closestvert,r,g,b,rgb,coladd;
	long		lp;

	shptr = &TribeBossShield[0];

	closestdist = 0x7fffffff;
	closestvert = 0;

	for (lp=0;lp<40;lp++)
	{
		long	dx,dy,dz;

		if (lp >=16 && lp <=23)
		{
			dx = (shptr->x+item->pos.x_pos) - x;
			dy = (shptr->y+item->pos.y_pos) - y;
			dz = (shptr->z+item->pos.z_pos) - z;

			dx = SQUARE(dx)+SQUARE(dy)+SQUARE(dz);
			if (dx < closestdist)
			{
				closestdist = dx;
				closestvert = lp;
			}
		}
		shptr++;
	}

	switch ( lara.gun_type )
	{
		case LG_PISTOLS:
			coladd = 144;
			break;

		case LG_MAGNUMS:
			coladd = 200;
	 		break;

		case LG_UZIS:
			coladd = 144;
			break;

		case LG_SHOTGUN:
			coladd = 192;
			break;

		case LG_HARPOON:
			coladd = 200;
			break;

		case LG_ROCKET:
			coladd = 224;
			break;

		case LG_GRENADE:
			coladd = 224;
			break;

		case LG_M16:
			coladd = 192;
			break;

		default:
			coladd = 0;
			break;
	}

	for (lp=0;lp<5;lp++)
	{
		long	vert;

		vert = closestvert+affected[lp];
		if ((vert & 7) == 7 && affected[lp] == -1)
			vert += 8;
		if ((vert & 7) == 0 && affected[lp] == 1)
			vert -= 8;
		shptr = &TribeBossShield[vert];
		rgb = shptr->rgb;
		r = rgb & 255;
		g = ((rgb)>>8) & 255;
		b = ((rgb)>>16) & 255;

		if (lp==0)
		{
			if (coladd >= 200)
				r = coladd;
			else
			{
				r += coladd>>2;
				if (r > coladd)
					r = coladd;
			}
		}
		else
		{
			if (coladd >= 200)
				r = coladd>>1;
			else
			{
				r += coladd>>3;
				if (r > coladd>>1)
					r = coladd>>1;
			}
		}

		if (lp==0)
		{
			if (coladd >= 200)
				g = coladd;
			else
			{
				g += coladd>>2;
				if (g > coladd)
					g = coladd;
			}
		}
		else
		{
			if (coladd >= 200)
				g = coladd>>1;
			else
			{
				g += coladd>>3;
				if (g > coladd>>1)
					g = coladd>>1;
			}
		}

		if (lp==0)
		{
			if (coladd >= 200)
				b = coladd;
			else
			{
				b += coladd>>2;
				if (b > coladd)
					b = coladd;
			}
		}
		else
		{
			if (coladd >= 200)
				b = coladd>>1;
			else
			{
				b += coladd>>3;
				if (b > coladd>>1)
					b = coladd>>1;
			}
		}

		shptr->rsub = (GetRandomControl()&7)+8;
		shptr->gsub = (GetRandomControl()&7)+8;
		shptr->bsub = (GetRandomControl()&7)+8;
		if (lara.gun_type == LG_ROCKET || lara.gun_type == LG_GRENADE)
		{
			shptr->rsub >>= 1;
			shptr->gsub >>= 1;
			shptr->bsub >>= 1;
		}
		shptr->rgb = r|(g<<8)|(b<<16);
	}

	pos.x = x;
	pos.y = y;
	pos.z = z;
	for (lp=0;lp<7;lp++)
		TriggerElectricSparks(&pos,1);
}

void RotateHeadXAngle(ITEM_INFO *item)
{
	PHD_VECTOR	pos1,pos2;
	long		x,y,z;
	sint16	angle;

	pos1.x = pos1.y = pos1.z = 0;
	GetJointAbsPosition(lara_item,&pos1,0);	// Get position of Lara's hips.
	pos2.x = pos2.y = pos2.z = 0;
	GetJointAbsPosition(item,&pos2,0);	// Get position of TribeBoss's head.

	x = abs(pos2.x - pos1.x);
	y = pos2.y - pos1.y;
	z = abs(pos2.z - pos1.z);

	x = mSqrt(SQUARE(x) + SQUARE(z));
	angle = phd_atan(x, y);			// Get X angle between them.

	if (abs(angle) < 0x2000)
		CreatureJoint(item, 2, angle);	// Rotate head to face Lara.
	else
		CreatureJoint(item, 2, 0);	// Rotate head to point forward.
}

void TriggerLizardMan()
{
	ROOM_INFO	*r;
	ITEM_INFO *item;
	sint16	room_number;

	item = &items[bossdata.LizmanItem];

	item->object_number = LIZARD_MAN;
	item->room_number = bossdata.LizmanRoom;	//## *Think* this should be removed
	item->pos.x_pos = lizman_summon_coords[bossdata.attack_type-1][0];
	item->pos.y_pos = lizman_summon_coords[bossdata.attack_type-1][1];
	item->pos.z_pos = lizman_summon_coords[bossdata.attack_type-1][2];
	item->anim_number = objects[item->object_number].anim_index;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = item->goal_anim_state = anims[item->anim_number].current_anim_state;
	item->required_anim_state = 0;
	item->pos.y_rot = (bossdata.attack_type == ATTACK_HAND1) ? 0x8000 : 0;
	item->pos.x_rot = item->pos.z_rot = 0;
	item->flags = item->timer = 0;
	item->data = NULL;
	item->mesh_bits = 0xffffffff;
	item->hit_points = objects[item->object_number].hit_points;
	item->collidable = 1;
	item->active = 0;
  	item->status = ACTIVE;
	AddActiveItem(bossdata.LizmanItem);
 	EnableBaddieAI(bossdata.LizmanItem,1);

	room_number = item->room_number;
	GetFloor(item->pos.x_pos,item->pos.y_pos,item->pos.z_pos,&room_number);
	if (item->room_number != room_number)
		ItemNewRoom(bossdata.LizmanItem, room_number);
	lizard_man_active = 1;

	RemoveDrawnItem(bossdata.LizmanItem);
	r = &room[item->room_number];				// Add Item to the
	item->next_item = r->item_number;           // Rooms Draw List
	r->item_number = bossdata.LizmanItem;

}


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

	TribeBossShieldOn = 0;

	if (	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,2,0);	// -2 = Set off a dynamic light controller.
		for (lp=0;lp<2;lp++)
			TriggerExplosionSparks(x, y, z,3,-1,2, 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.

	shptr = &TribeBossShield[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);
				b = (GetRandomDraw()&63)+224;
				g = (b>>2)+(GetRandomDraw()&63);
				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++;
		}
	}
}



void TriggerSummonSmoke(long x, long y, long z)
{
	SPARKS	*sptr;
	long		size;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 16;
	sptr->sG = 64;
	sptr->sB = 0;

	sptr->dR = 8;
	sptr->dG = 32;
	sptr->dB = 0;

	sptr->ColFadeSpeed = 16 + (GetRandomControl()&7);
	sptr->FadeToBlack = 64;
	sptr->sLife = sptr->Life = (GetRandomControl()&15)+96;

	sptr->TransType = COLADD;

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

	sptr->x = x + ((GetRandomControl()&127)-64);
	sptr->y = y - (GetRandomControl()&31);
	sptr->z = z + ((GetRandomControl()&127)-64);
	sptr->Xvel = ((GetRandomControl()&255)-128);
	sptr->Yvel = -(GetRandomControl()&15)-16;
	sptr->Zvel = ((GetRandomControl()&255)-128);
	sptr->Friction = 0;

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

	sptr->Def = objects[EXPLOSION1].mesh_index;
	sptr->Scalar = 3;
	sptr->Gravity = -(GetRandomControl()&7)-8;
	sptr->MaxYvel = -(GetRandomControl()&7)-4;
	size = (GetRandomControl()&31)+128;
	sptr->Width = sptr->sWidth = size>>1;
	sptr->dWidth = size;
	size += (GetRandomControl()&31)+32;
 	sptr->Height = sptr->sHeight = size>>1;
	sptr->dHeight = size;
}


sint16 FindLizardManItemNumber(sint16 room_number)
{
	ITEM_INFO *target;
	sint16 target_number;

	for (target_number = 0; target_number < level_items; target_number++)
	{
		target = &items[target_number];
		if (target->object_number == LIZARD_MAN && target->room_number == room_number)
			return (target_number);
	}

#ifdef GAMEDEBUG
	wsprintf(exit_message, "Didn't find Lizard-Man for Tribeboss!!");
	S_ExitSystem(exit_message);
#endif

	return(-1);	// Just to remove warning!
}


