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

typedef unsigned char uchar;

#endif

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

static void WillBossDie(sint16 item_number);
static void ExplodeWillBoss(ITEM_INFO *item);
#ifdef PSX_VERSION
static void DrawWillBossShield(ITEM_INFO *item);
static void DrawWillBossShield(ITEM_INFO *item);
static void DrawExplosionRings();
#endif

static void TriggerPlasma(sint16 item_number, long node, long size);
static void TriggerPlasmaBallFlame(sint16 fx_number, long type, long xv, long yv, long zv);
static void TriggerPlasmaBall(PHD_VECTOR *pos, sint16 room_number, sint16 angle, sint16 type);

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 ****************************************/

enum	{	ATTACK_HEAD, ATTACK_HAND1, ATTACK_HAND2	};

// Tribe boss.

enum tribeboss_anims {WILLBOSS_STOP, WILLBOSS_WALK, WILLBOSS_LUNGE, WILLBOSS_BIGKILL, WILLBOSS_STUNNED, WILLBOSS_KNOCKOUT, WILLBOSS_GETUP, WILLBOSS_WALKATAK1, WILLBOSS_WALKATAK2, WILLBOSS_180, WILLBOSS_SHOOT};

#define MAX_TRIGGER_RANGE	0x4000

#define WILLBOSS_TURN 		(ONE_DEGREE*5)
#define WILLBOSS_ATTACK_TURN	(ONE_DEGREE*2)
#define WILLBOSS_TOUCH		(0x900000)
#define WILLBOSS_BITE_DAMAGE 220
#define WILLBOSS_TOUCH_DAMAGE 10
#define WILLBOSS_ATTACK_RANGE SQUARE(WALL_L*3/2)
#define WILLBOSS_LUNGE_RANGE SQUARE(WALL_L*2)
#define WILLBOSS_FIRE_RANGE SQUARE(WALL_L*4)
#define WILLBOSS_STOP_ANIM 	0
#define WILLBOSS_KILL_ANIM 	6
#define WILLBOSS_HP_AFTER_KO 200
#define WILLBOSS_KO_TIME 280
#define WILLBOSS_PATH_DISTANCE 1024
#define NO_AI_PATH -1
#define WILLBOSS_STUN_ANIM	7

//#define DEBUG_WILLBOSS

#ifdef DEBUG_WILLBOSS
extern char exit_message[];
#endif

#ifdef DEBUG_WILLBOSS
static char *WillbossStrings[] = {"STOP", "WALK", "LUNGE", "BIGKILL", "STUNNED", "KNOCKOUT", "GETUP", "WALKATAK1", "WALKATAK2", "180", "SHOOT"};
#endif

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

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

void InitialiseWillBoss(sint16 item_number);
void WillBossControl(sint16 item_number);
void S_DrawWillBoss(ITEM_INFO *item);
void ControlWillbossPlasmaBall(sint16 fx_number);

void *func[] __attribute__((section(".header"))) = {
	&InitialiseWillBoss,
	&WillBossControl,
	&S_DrawWillBoss,
	&ControlWillbossPlasmaBall,
};

#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 BITE_INFO willboss_points[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 puzzle_complete;

PHD_VECTOR	TrigDynamics[5];	// 0 - Electric from head
							// 1 - Electric beam hitting wall.
							// 2 - Boss firing electric beam.
							// 3 - Lara being electricuted.
							// 4 - Lara hit by 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	WillBossShield[40];	// x,y,z,rgb.
static EXPLOSION_RING	ExpRings[7];

static PHD_3DPOS ai_path[16];
static PHD_3DPOS ai_junction[4];
static int junction_index[4];
static int closest_ai_path = NO_AI_PATH;
static int lara_ai_path = NO_AI_PATH;
static int lara_junction = NO_AI_PATH;
static int direction = 1;
static int desired_direction = 1;
static BITE_INFO willboss_bite_left = {19,-13,3, 20};
static BITE_INFO willboss_bite_right = {19,-13,3, 23};

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

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

void WillBossControl(sint16 item_number)
{
	ITEM_INFO *item;
	CREATURE_INFO *willboss;
	sint16 angle, head, tilt,i, linknum, pathnum, juncnum, old_closest, pathdiff, lara_alive, in_fire_zone;
	AI_INFO info;
	sint32 distance, best_distance, best_junc_distance, will2junc_distance, x, z;

	if (!CreatureActive(item_number))
		return;

	item = &items[item_number];
	willboss = (CREATURE_INFO *)item->data;
	head = angle = tilt = 0;
	lara_alive = (lara_item->hit_points>0);

	if (closest_ai_path == NO_AI_PATH)
	{
		for (linknum = room[ item->room_number ].item_number, pathnum=0, juncnum=0 ; linknum!=NO_ITEM; linknum=items[linknum].next_item )
		{
			if (items[linknum].object_number == AI_X1 && pathnum < 16)
			{
				ai_path[pathnum] = items[linknum].pos;
				pathnum++;
			}
			else if (items[linknum].object_number == AI_X2 && juncnum < 4)
			{
				ai_junction[juncnum] = items[linknum].pos;
				juncnum++;
			}
		}

		closest_ai_path = -1; //Initialise boss path ... which AI_X1 point is he closest to?
		best_distance = 0x7fffffff;
		for (i=0; i<16; i++)
		{
			x = (ai_path[i].x_pos - item->pos.x_pos) >> 6;
			z = (ai_path[i].z_pos - item->pos.z_pos) >> 6;
			distance = x*x + z*z;
			if (distance < best_distance)
			{
				closest_ai_path = i;
				best_distance = distance;
			}
		}

		lara_ai_path = -1; //Initialise lara path info ... which AI_X1 point is she closest to?
		best_distance = 0x7fffffff;
		for (i=0; i<16; i++)
		{
			x = (ai_path[i].x_pos - lara_item->pos.x_pos) >> 6;
			z = (ai_path[i].z_pos - lara_item->pos.z_pos) >> 6;
			distance = x*x + z*z;
			if (distance < best_distance)
			{
				lara_ai_path = i;
				best_distance = distance;
			}
		}

		for (juncnum = 0; juncnum<4; juncnum++)
		{
			pathnum = -1; //Initialise junction index ... which AI_X1 point is each AI_X2 point closest to?
			best_distance = 0x7fffffff;
			for (i=0; i<16; i++)
			{
				x = abs((ai_path[i].x_pos - ai_junction[juncnum].x_pos) >> 6);
				z = abs((ai_path[i].z_pos - ai_junction[juncnum].z_pos) >> 6);
				if (x > z)
					distance = x + (z >> 1);
				else
					distance = x + (z >> 1);
				if (distance < best_distance)
				{
					pathnum = i;
					best_distance = distance;
				}
			}
			junction_index[juncnum] = pathnum;
		}


#ifdef DEBUG_WILLBOSS
	printf("\nspiderX:%d, spiderZ:%d, closest point:%d\n 	", item->pos.x_pos, item->pos.z_pos, closest_ai_path);

	for (i=0; i<16; i++)
		printf("Path point %d - X: %d, Z:%d, ANGLE:%d\n", i, ai_path[i].x_pos, ai_path[i].z_pos, ai_path[i].y_rot);
	for (i=0; i<4; i++)
		printf("Junction %d - X: %d, Z:%d, ANGLE:%d, JuncIndex: %d\n", i, ai_junction[i].x_pos, ai_junction[i].z_pos, ai_junction[i].y_rot, junction_index[i]);
#endif

	}

	//Move AI circle code to here..

	best_distance = 0x7fffffff;
	old_closest = closest_ai_path;				// **** Work out the closest path point to the Boss ****
	for (i=old_closest-1; i<old_closest+2; i++) //The closest point must always be either the same as last time, or one of the points on either side
	{
		if (i < 0)
			pathnum = i + 16;
		else if (i > 15)
			pathnum = i - 16;
		else
			pathnum = i;

		x = (ai_path[pathnum].x_pos - item->pos.x_pos) >> 6;
		z = (ai_path[pathnum].z_pos - item->pos.z_pos) >> 6;
		distance = x*x + z*z;
		if (distance < best_distance)
		{
			closest_ai_path = pathnum;
			best_distance = distance;
		}
	}
	//	angle = ai_path[closest_ai_path].y_rot - item->pos.y_rot;

	best_distance = 0x7fffffff;
	old_closest = lara_ai_path;				// **** Work out the closest path point to Lara ****
	for (i=old_closest-1; i<old_closest+2; i++) //The closest point must always be either the same as last time, or one of the points on either side
	{
		if (i < 0)
			pathnum = i + 16;
		else if (i > 15)
			pathnum = i - 16;
		else
			pathnum = i;

		x = (ai_path[pathnum].x_pos - lara_item->pos.x_pos) >> 6;
		z = (ai_path[pathnum].z_pos - lara_item->pos.z_pos) >> 6;
		distance = x*x + z*z;
		if (distance < best_distance)
		{
			lara_ai_path = pathnum;
			best_distance = distance;
		}
	}

	best_junc_distance = 0x7fffffff;
	// **** Work out the closest junction point to Lara ****
	for (i=0; i<4; i++)
	{
		x = (ai_junction[i].x_pos - lara_item->pos.x_pos) >> 6;
		z = (ai_junction[i].z_pos - lara_item->pos.z_pos) >> 6;
		distance = (x*x) + (z*z);
		if (distance < best_junc_distance)
		{
			lara_junction = i;
			best_junc_distance = distance;
		}
	}

	in_fire_zone = ((best_junc_distance < best_distance) || (item->pos.y_pos > lara_item->pos.y_pos + WALL_L*2)); //i.e. she's closer to an AI_X2 point than she is to any AI_X1 point
	x = ai_junction[lara_junction].x_pos - item->pos.x_pos;
	z = ai_junction[lara_junction].z_pos - item->pos.z_pos;
	will2junc_distance = (x*x) + (z*z);

	if (item->hit_points <= 0)
	{
		puzzle_complete = Inv_RequestItem(ICON_PICKUP1_ITEM);
		puzzle_complete += Inv_RequestItem(ICON_PICKUP2_ITEM);
		puzzle_complete += Inv_RequestItem(ICON_PICKUP3_ITEM);
		puzzle_complete += Inv_RequestItem(ICON_PICKUP4_ITEM);

		if (puzzle_complete != 4 || item->item_flags[1] == 0)
		{
			willboss->maximum_turn = 0;
			switch (item->current_anim_state)
			{
				case WILLBOSS_STOP:	// Waiting.
					item->goal_anim_state = WILLBOSS_STUNNED;
					break;
				case WILLBOSS_STUNNED:	// Waiting.
					bossdata.death_count = WILLBOSS_KO_TIME;
					break;
				case WILLBOSS_KNOCKOUT:	// Waiting.
					if (bossdata.death_count-- < 0)
						item->goal_anim_state = WILLBOSS_GETUP;
					break;
				case WILLBOSS_GETUP:	// Waiting.
					item->hit_points = WILLBOSS_HP_AFTER_KO;
					if (puzzle_complete == 4)
						item->item_flags[1] = 1;
					willboss->maximum_turn = WILLBOSS_ATTACK_TURN;
					break;
			 	default:
					item->goal_anim_state = WILLBOSS_STOP;
			}
		}
		else
		{
			if (item->current_anim_state != WILLBOSS_STUNNED)
			{
				item->anim_number = objects[item->object_number].anim_index + WILLBOSS_STUN_ANIM;
				item->frame_number = anims[item->anim_number].frame_base;
				item->current_anim_state = WILLBOSS_STUNNED;
			}
			else if (item->frame_number >= anims[item->anim_number].frame_end - 2)
			{
				long lp;

				item->frame_number = anims[item->anim_number].frame_end - 2;
				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.explode_count < 256)
					bossdata.explode_count++;

				if (	bossdata.explode_count > 128 && bossdata.ring_count == 6 && ExpRings[5].life == 0)
				{
					WillBossDie(item_number);
					bossdata.dead = 1;
				}
				else
					ExplodeWillBoss(item);
				return;
			}
		}
	}
	else
	{
//		if (item->hit_status)
//			direction = -1;
		CreatureAIInfo(item, &info);
/*
			if (angle > willboss->maximum_turn)
				angle = willboss->maximum_turn;
			else if (angle < -willboss->maximum_turn)
				angle = -willboss->maximum_turn;
*/

		if (item->touch_bits)
			lara_item->hit_points -= WILLBOSS_TOUCH_DAMAGE;

		pathdiff = lara_ai_path - closest_ai_path;

		if (direction == -1 && ((pathdiff < 0 && pathdiff > -6) || pathdiff > 10))
			desired_direction = 1;
		else if (direction == 1 && ((pathdiff > 0 && pathdiff < 6) || pathdiff < -10))
			desired_direction = -1;
/*
		if (just_did_180 && item->frame_number == anims[objects[item->object_number].anim_index + WILLBOSS_STOP_ANIM].frame_base)
		{
			just_did_180 = FALSE;
			if (item->pos.y_rot < 0)
				item->pos.y_rot += 0x8000;
			else
				item->pos.y_rot -= 0x8000;
			direction *= -1;
		}
*/
		willboss->target.x = ai_path[closest_ai_path].x_pos + (direction * WILLBOSS_PATH_DISTANCE * phd_sin(ai_path[closest_ai_path].y_rot) >> W2V_SHIFT);
		willboss->target.z = ai_path[closest_ai_path].z_pos + (direction * WILLBOSS_PATH_DISTANCE * phd_cos(ai_path[closest_ai_path].y_rot) >> W2V_SHIFT);

		switch (item->current_anim_state)
		{
			case WILLBOSS_STOP:	// Waiting.
				willboss->maximum_turn = 0;
				willboss->flags = 0;
				if (direction != desired_direction)
				{
					item->goal_anim_state = WILLBOSS_180;
				//	direction = desired_direction;
				}
				else if (in_fire_zone && info.ahead && will2junc_distance < WILLBOSS_FIRE_RANGE && lara_item->hit_points > 0)
					item->goal_anim_state = WILLBOSS_SHOOT;
				else if (info.bite && info.distance < WILLBOSS_LUNGE_RANGE)
					item->goal_anim_state = WILLBOSS_LUNGE;
				else
					item->goal_anim_state = WILLBOSS_WALK;
				break;
			case WILLBOSS_WALK:	// Walking.
				willboss->maximum_turn = WILLBOSS_TURN;
				willboss->flags = 0;

				if (direction != desired_direction)
				{
					item->goal_anim_state = WILLBOSS_STOP;
				//	direction = desired_direction;
				}
				else if (in_fire_zone && info.ahead && will2junc_distance < WILLBOSS_FIRE_RANGE)
					item->goal_anim_state = WILLBOSS_STOP;
				else if (info.bite && info.distance < WILLBOSS_ATTACK_RANGE)
				{
					if ((GetRandomControl() & 3) == 1 )
						item->goal_anim_state = WILLBOSS_STOP;
					else if (item->frame_number < anims[item->anim_number].frame_base + 30)
						item->goal_anim_state = WILLBOSS_WALKATAK2;
					else
						item->goal_anim_state = WILLBOSS_WALKATAK1;
				}
				break;
			case WILLBOSS_180:	// Flying 180 degree leap!
				willboss->maximum_turn = 0;
				willboss->flags = 0;

				if (item->frame_number == anims[item->anim_number].frame_base + 51) // final frame of 180 leap - so turn it around
				{
					item->pos.y_rot += 0x8000;
					direction = -direction;
				}
				break;
			case WILLBOSS_LUNGE:	// Attacks...
				willboss->target.x = lara_item->pos.x_pos;
				willboss->target.z = lara_item->pos.z_pos;
				willboss->maximum_turn = WILLBOSS_ATTACK_TURN;

				if (!willboss->flags && (item->touch_bits & WILLBOSS_TOUCH))
				{
					lara_item->hit_status = 1;
					lara_item->hit_points -= WILLBOSS_BITE_DAMAGE*2;
					CreatureEffect(item, &willboss_bite_left, DoBloodSplat);
					CreatureEffect(item, &willboss_bite_right, DoBloodSplat);
				//	lara.poisoned = 0x100;	// Set to max poisoning, just for a laugh

					willboss->flags = 1;
				}
				break;
			case WILLBOSS_WALKATAK1:
			case WILLBOSS_WALKATAK2:

				if (!willboss->flags && (item->touch_bits & WILLBOSS_TOUCH))
				{
					lara_item->hit_status = 1;
					lara_item->hit_points -= WILLBOSS_BITE_DAMAGE;
					CreatureEffect(item, &willboss_bite_left, DoBloodSplat);
					CreatureEffect(item, &willboss_bite_right, DoBloodSplat);

					willboss->flags = 1;
				}
				if (in_fire_zone && info.bite && will2junc_distance < WILLBOSS_FIRE_RANGE)
					item->goal_anim_state = WILLBOSS_WALK;
				else if (info.bite && info.distance < WILLBOSS_ATTACK_RANGE)
				{
					if (item->current_anim_state == WILLBOSS_WALKATAK1)
						item->goal_anim_state = WILLBOSS_WALKATAK2;
					else
						item->goal_anim_state = WILLBOSS_WALKATAK1;
				}
				else
					item->goal_anim_state = WILLBOSS_WALK;
				break;
			case WILLBOSS_BIGKILL:
				switch(item->frame_number - anims[item->anim_number].frame_base)
				{
					case 0:
					case 43:
					case 95:
					case 105:
						CreatureEffect(item, &willboss_bite_left, DoBloodSplat);
						break;
					case 61:
					case 91:
					case 101:
						CreatureEffect(item, &willboss_bite_right, DoBloodSplat);
						break;
				}
				break;
			case WILLBOSS_SHOOT:

				willboss->target.x = lara_item->pos.x_pos;
				willboss->target.z = lara_item->pos.z_pos;
				willboss->maximum_turn = WILLBOSS_ATTACK_TURN;
				if (item->frame_number - anims[item->anim_number].frame_base == 40 && lara_item->hit_points > 0)
				{
					PHD_VECTOR	pos;
					pos.x = -64;
					pos.y = 410;
					pos.z = 0;
					GetJointAbsPosition( item, &pos, 20);
					TriggerPlasmaBall(&pos, item->room_number, item->pos.y_rot - 0x1000,0);
					pos.x = 64;
					pos.y = 410;
					pos.z = 0;
					GetJointAbsPosition( item, &pos, 23);
					TriggerPlasmaBall(&pos, item->room_number, item->pos.y_rot + 0x1000,0);
				}

		 		{
					PHD_VECTOR	pos;
					long	bright,r,g,b,rnd;

					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();
					g=31-((rnd>>4)&3);
					b=24-((rnd>>6)&3);
					r=rnd&7;
					r = (r*bright)>>4;
					g = (g*bright)>>4;
					b = (b*bright)>>4;

					pos.x = pos.y = pos.z = 0;
					GetJointAbsPosition( item, &pos, 17);
					TriggerDynamic(pos.x,pos.y,pos.z,12,r,g,b);
					TriggerPlasma(item_number, SPN_WILLBOSSLPLASMA, bright<<2);
					TriggerPlasma(item_number, SPN_WILLBOSSRPLASMA, bright<<2);
				}

				break;
		}

 		/* Is Lara dead? We've killed her! */
		if (lara_alive && lara_item->hit_points <= 0)
		{
			CreatureKill(item, WILLBOSS_KILL_ANIM, WILLBOSS_BIGKILL, EXTRA_YETIKILL); // uses EXTRA_YETIKILL slot
			willboss->maximum_turn = 0;

			return;
		}



//		if (info.ahead)
//			head = info.angle;
/*
		switch (item->current_anim_state)
		{
			case WILLBOSS_WAIT:	// Waiting.
				willboss->maximum_turn = 0;

				if (item->goal_anim_state != WILLBOSS_RISE && attack_flag)
					item->goal_anim_state = WILLBOSS_RISE;
				break;

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

			case WILLBOSS_FLOAT:	// Rising.
				willboss->maximum_turn = WILLBOSS_TURN;
				break;

			default:
				break;
		}
		*/
	}
	#ifdef DEBUG_WILLBOSS
	sprintf(exit_message, "Mood: %d", willboss->mood);
	PrintDbug(2, 2, exit_message);
	sprintf(exit_message, "%s", WillbossStrings[item->current_anim_state]);
	PrintDbug(2, 3, exit_message);
	sprintf(exit_message, "%s", WillbossStrings[item->goal_anim_state]);
	PrintDbug(2, 4, exit_message);
	sprintf(exit_message, "LaraPath:%d, Dist:%d", lara_ai_path, best_distance);
	PrintDbug(2, 5, exit_message);
	sprintf(exit_message, "LaraJunc:%d, Dist:%d", lara_junction, best_junc_distance);
	PrintDbug(2, 6, exit_message);
	sprintf(exit_message, "Fire_Zone: %d", in_fire_zone);
	PrintDbug(2,7, exit_message);

//	sprintf(exit_message, "%s", WillbossStrings[item->required_anim_state]);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Anim Number; %d", item->anim_number - objects[WILLARD_BOSS].anim_index);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Alert:%d, Goal:%d, Hurt:%d", willboss->alerted, willboss->reached_goal, willboss->hurt_by_lara);
//	PrintDbug(2,6, exit_message);

//	sprintf(exit_message, "AI Bits: %d", item->ai_bits);
//	PrintDbug(2,7, exit_message);
	#endif

	angle = CreatureTurn(item, willboss->maximum_turn);


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

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

	if (bossdata.explode_count)
		DrawExplosionRings();

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

static void WillBossDie(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 InitialiseWillBoss(sint16 item_number)
{

	closest_ai_path = NO_AI_PATH;
	items[item_number].item_flags[1] = 0; //use this cos nothing else does
	bossdata.dead = bossdata.dropped_icon = bossdata.explode_count = 0;
/*
	SHIELD_POINTS	*shptr;
	long		lp,lp1,angle,y;

	attack_flag = bossdata.death_count = bossdata.explode_count = bossdata.ring_count = 0;

// Initialise shield coordinates.

	shptr = &WillBossShield[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 DrawWillBossShield(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 = &WillBossShield[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 = &WillBossShield[0];
	shptr2 = &WillBossShield[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 ExplodeWillBoss(ITEM_INFO *item)
{
	SHIELD_POINTS	*shptr;
	long		lp,lp1,angle,y,r,g,b;

	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++;

		for (x=0;x<24;x+=3)
		{
			PHD_VECTOR	pos;
			pos.x = 0;
			pos.y = 0;
			pos.z = 0;
			GetJointAbsPosition(item, &pos, x);
			TriggerPlasmaBall(&pos, item->room_number, GetRandomControl()<<1, 4);
		}

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

	    	SoundEffect( 76, &item->pos,PITCH_SHIFT|0x800000);
//		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);

	}

// 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 = &WillBossShield[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)
			{
				g = (GetRandomDraw()&31)+224;
				b = (g>>1) + (GetRandomDraw()&63);
				r = (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++;
		}
	}
}

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

				g = (GetRandomDraw()&31)+224;
				b = (g>>1) + (GetRandomDraw()&63);
				r = (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 TriggerPlasma(sint16 item_number, long node, 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_TRIGGER_RANGE || dx > MAX_TRIGGER_RANGE || dz < -MAX_TRIGGER_RANGE || dz > MAX_TRIGGER_RANGE)
		return;

	sptr = &spark[GetFreeSpark()];

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

	sptr->dG = 192+(GetRandomControl()&63);
	sptr->dB = 128+(GetRandomControl()&63);
	sptr->dR = 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()&31)-16);
	sptr->Yvel = (GetRandomControl()&7)+8;
	sptr->Zvel = ((GetRandomControl()&31)-16);
	sptr->Friction = 3;

	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()&7)+8;
	sptr->MaxYvel = (GetRandomControl()&7)+16;

	sptr->FxObj = item_number;
	sptr->NodeNumber = node;

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


static void TriggerPlasmaBallFlame(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_TRIGGER_RANGE || dx > MAX_TRIGGER_RANGE || dz < -MAX_TRIGGER_RANGE || dz > MAX_TRIGGER_RANGE)
		return;

	sptr = &spark[GetFreeSpark()];

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

	sptr->dG = 192+(GetRandomControl()&63);
	sptr->dB = 128+(GetRandomControl()&63);
	sptr->dR = 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)<<1;
	sptr->Yvel = (GetRandomControl()&511) - 256;
	sptr->Zvel = (zv + (GetRandomControl()&255) - 128)<<1;
	sptr->Friction = 5|(5<<4);

	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->Gravity = sptr->MaxYvel = 0;

	if (type >= 0)
	{
		sptr->Scalar = 3;
		size = type;
	}
	else
	{
		if (type < -2)
			sptr->Scalar = 4;
		else
			sptr->Scalar = 2;
		size = (GetRandomControl()&15)+16;
		sptr->Xvel = xv + (GetRandomControl()&255) - 128;
		sptr->Yvel = yv;
		sptr->Zvel = zv + (GetRandomControl()&255) - 128;
		sptr->Friction = 5;
	}

	sptr->Width = sptr->sWidth = size;
 	sptr->Height = sptr->sHeight = size;
	sptr->dWidth = size>>3;
	sptr->dHeight = size>>3;
}

static void TriggerPlasmaBall(PHD_VECTOR *pos, sint16 room_number, sint16 angle, sint16 type)
{
	FX_INFO 	*fx;
	sint16 	fx_number;

	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.x_rot = 0;
		fx->pos.y_rot = angle;
		fx->object_number = EXTRAFX2;
		fx->speed = 16 + (type) ? (GetRandomControl()&31)+16 : 0;
		fx->fallspeed = -(type<<4);
		fx->flag1 = type;
	}
}

void ControlWillbossPlasmaBall(sint16 fx_number)
{
	FLOOR_INFO	*floor;
	FX_INFO	*fx;
	long		old_x, old_y, old_z, speed;
	long		tx,ty,tz;
	long		rnd,r,g,b;
	uchar 	radtab[5] = {	13,7,7,7,7	};
	sint16 	room_number;
	sint16 	angles[2];

	fx = &effects[fx_number];

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

	// Work out target.

	if (fx->flag1 == 0)
	{
		tx = lara_item->pos.x_pos;
		ty = lara_item->pos.y_pos - 256;
		tz = lara_item->pos.z_pos;

		phd_GetVectorAngles( tx-fx->pos.x_pos, ty-fx->pos.y_pos, tz-fx->pos.z_pos, angles );
		fx->pos.y_rot = angles[0];
		fx->pos.x_rot = angles[1];
		if (fx->speed < 512)
			fx->speed+=(fx->speed>>4)+4;
		if (wibble&4)
			TriggerPlasmaBallFlame(fx_number,fx->speed>>1,0,0,0);
	}
	else
	{
		fx->fallspeed += (fx->flag1 == 1) ? 1:2;
		if ((wibble & 12)==0)
		{
			if (fx->speed)
				fx->speed--;
			TriggerPlasmaBallFlame(fx_number,-1-fx->flag1,0,-(GetRandomControl()&31),0);
		}
	}

	speed = (fx->speed * phd_cos(fx->pos.x_rot)) >> W2V_SHIFT;
	fx->pos.z_pos += (speed * phd_cos(fx->pos.y_rot)) >> W2V_SHIFT;
	fx->pos.x_pos += (speed * phd_sin(fx->pos.y_rot)) >> W2V_SHIFT;
	fx->pos.y_pos += -((fx->speed * phd_sin(fx->pos.x_rot)) >> W2V_SHIFT) + fx->fallspeed;

	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))
	{
		PHD_VECTOR	pos;
		if (fx->flag1 == 0)
		{
			pos.x = old_x;
			pos.y = old_y;
			pos.z = old_z;
			r = 2+(GetRandomControl()&3);

			for (g=0;g<r;g++)
				TriggerPlasmaBall(&pos, fx->room_number, (fx->pos.y_rot)+0x6000+(GetRandomControl()&0x3fff),1);
		}
		KillEffect(fx_number);
		return;
	}

	if (ItemNearLara(&fx->pos, 200) && !fx->flag1)
	{
		PHD_VECTOR	pos;

		for (g=14;g>=0;g-=2)	// Loop through all of Lara's nodes.
		{
			pos.x = 0;
			pos.y = 0;
			pos.z = 0;
			GetJointAbsPosition(lara_item, &pos, g);
			TriggerPlasmaBall(&pos, fx->room_number, GetRandomControl()<<1,1);
		}

		LaraBurn();
		lara_item->hit_points = -1;
		lara.BurnGreen = 1;
		KillEffect(fx_number);
		return;
	}

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

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


