/*********************************************************************************************/
/*                                                                                           */
/* AI functions                                                                              */
/*                                                                                           */
/*********************************************************************************************/

/*---------------------------------------------------------------------------
 *	Import Headers
\*--------------------------------------------------------------------------*/

#ifdef PSX_VERSION
#include "../spec_psx/typedefs.h"
#else
#include "../specific/stypes.h"
#endif

#include "objects.h"
#include "lara.h"
#include "laraanim.h"
#include "control.h"
#include "camera.h"
#include "sphere.h"
#include "lot.h"

/********************************** GLOBAL VARIABLES *****************************************/

/* Box stuff for baddie smarts */
int number_boxes;
BOX_INFO *boxes;
uint16 *overlap;
sint16 *ground_zone[4][2], *fly_zone[2];

/* CalculateTarget() clip values */
#define CLIP_LEFT   1
#define CLIP_RIGHT  2
#define CLIP_TOP    4
#define CLIP_BOTTOM 8
#define ALL_CLIP (CLIP_LEFT|CLIP_RIGHT|CLIP_TOP|CLIP_BOTTOM)
#define SECONDARY_CLIP 16

#define BIFF      (WALL_L>>1)
#define BIFF_AVOID_TURN 0x800

/* Limit amount that flying/swimming baddies tilt forward and backward */
#define MAX_X_ROT (ONE_DEGREE*20)

#define CREATURE_FLOAT_SPEED (WALL_L/32)

#define HEAD_ARC 0x3000
#define JOINT_ARC 0x3000
#define FEELER_DISTANCE 512
#define FEELER_ANGLE (45*ONE_DEGREE)
#define REACHED_GOAL_RADIUS (STEP_L*3)
#define GUNFIRE_ALERT_RADIUS 8000
#define PREDICTIVE_SCALE_FACTOR 14

#define NULL_OBJECT -1
//#define DEBUG_BOX

#ifdef DEBUG_BOX
extern char exit_message[];
#endif


/*********************************** FUNCTION CODE *******************************************/

#define STALK_DIST (WALL_L*3)


static int StalkBox(ITEM_INFO *item, ITEM_INFO *enemy, sint16 box_number)
{
/* Valid box for stalking is one close to enemy and not in its forward quadrant.
	If baddie is in forward quadrant, can only go to one of enemy's side quadrants */
/* LATEST: Got rid of quad restriction as many baddies are shooters now */
	BOX_INFO *box;
	int baddie_quad, box_quad, enemy_quad;
	sint32 x, z, xrange, zrange;

	// Catch another NULL Ptr... yet again.....
	if(enemy == NULL ) return 0;

	box = &boxes[box_number];
	z = (( (uint32)box->left + (uint32)box->right ) << (WALL_SHIFT-1)) - enemy->pos.z_pos;
	x = (( (uint32)box->top + (uint32)box->bottom ) << (WALL_SHIFT-1)) - enemy->pos.x_pos;
	zrange = STALK_DIST + (( (uint32)box->right - (uint32)box->left ) << WALL_SHIFT);
	xrange = STALK_DIST + (( (uint32)box->bottom - (uint32)box->top ) << WALL_SHIFT);

	/* Is box close enough to enemy for consideration? */
	if (x > xrange || x < -xrange || z > zrange || z < -zrange)
		return (0);

#define QUAD_RESTRICTION
#ifdef QUAD_RESTRICTION
	enemy_quad = (enemy->pos.y_rot >> 14) + 2;

	if (z>0)
		box_quad = (x>0)? 2 : 1;
	else
		box_quad = (x>0)? 3 : 0;

	/* Check if in reasonable quadrant (not enemy's front one) */
	if (enemy_quad == box_quad)
		return (0);


	if (item->pos.z_pos > enemy->pos.z_pos)
		baddie_quad = (item->pos.x_pos > enemy->pos.x_pos)? 2 : 1;
	else
		baddie_quad = (item->pos.x_pos > enemy->pos.x_pos)? 3 : 0;

	/* If baddie in enemy's front quad, then only try to go to side quad */
	if (enemy_quad == baddie_quad && abs(enemy_quad - box_quad) == 2)
		return (0);
#endif
	return (1);
}


void InitialiseCreature(sint16 item_number)
{
	/* Default setup of creature structure */
	ITEM_INFO *item;

	item = &items[item_number];

	/* Apply +/-45 degree jiggle to stop beastie being aligned to grid */
	if (item->object_number != ELECTRIC_CLEANER && item->object_number != SHIVA && item->object_number != TARGETS)
		item->pos.y_rot += (GetRandomControl() - 0x4000) >> 1;
	item->collidable = 1;
	item->data = NULL;
}


int CreatureActive(sint16 item_number)
{
	/* If creature hasn't got a slot allocated, then try to activate it */
	ITEM_INFO *item;

	item = &items[item_number];

	if (item->status == INVISIBLE)
	{
		if (!EnableBaddieAI(item_number, 0))
			return (0);
		item->status = ACTIVE;
	}

	return (1);
}


void CreatureAIInfo(ITEM_INFO *item, AI_INFO *info)
{
	/* Put together all the information needed for a baddie to be bad */
	ROOM_INFO *r;
	sint32 x, y, z;
	sint16 angle, lara_anim, *zone;
	CREATURE_INFO *creature;
	OBJECT_INFO *object;
	ITEM_INFO *enemy;

	if (item->data == NULL)
		return;

	creature = (CREATURE_INFO *)item->data;

	enemy = creature->enemy;

	if (!enemy)
	{
		enemy = lara_item; // no target?
		creature->enemy = lara_item; // TS - previously it didn't actually set this
	}

	if (creature->LOT.fly == NO_FLYING)
		zone = ground_zone[ZONE(creature->LOT.step)][flip_status];
	else
		zone = ground_zone[ZONE(1)][0];	//Shouldn't ever need this, but just to prevent NULL pointer worries


	/* Basic information about baddie */
	r = &room[item->room_number];
	item->box_number = r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) +
		((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].box;
	if (creature->LOT.fly == NO_FLYING)
		info->zone_number = zone[item->box_number];
	else
		info->zone_number = FLY_ZONE;

	/* Basic information about enemy */
	r = &room[enemy->room_number];
	enemy->box_number = r->floor[((enemy->pos.z_pos - r->z) >> WALL_SHIFT) +
		((enemy->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].box;
	if (creature->LOT.fly == NO_FLYING)
		info->enemy_zone = zone[enemy->box_number];
	else
		info->enemy_zone = FLY_ZONE;

	/* If Lara is on blocked off box, then she is in a safe zone that baddies can't get at */
	if (!objects[item->object_number].non_lot)
	{
		if (boxes[enemy->box_number].overlap_index & creature->LOT.block_mask)
			info->enemy_zone |= BLOCKED;
		else if (creature->LOT.node[item->box_number].search_number == (creature->LOT.search_number | BLOCKED_SEARCH))
			info->enemy_zone |= BLOCKED;
	}

	/* Relative squared distance and facing information */
	object = &objects[item->object_number];

//	z = enemy->pos.z_pos - (item->pos.z_pos + (object->pivot_length * phd_cos(item->pos.y_rot) >> W2V_SHIFT));
//	x = enemy->pos.x_pos - (item->pos.x_pos + (object->pivot_length * phd_sin(item->pos.y_rot) >> W2V_SHIFT));
	if (enemy == lara_item)
	{
		z = (enemy->pos.z_pos + (enemy->speed * PREDICTIVE_SCALE_FACTOR * phd_cos(lara.move_angle) >> W2V_SHIFT)) - (item->pos.z_pos + (object->pivot_length * phd_cos(item->pos.y_rot) >> W2V_SHIFT));
		x = (enemy->pos.x_pos + (enemy->speed * PREDICTIVE_SCALE_FACTOR * phd_sin(lara.move_angle) >> W2V_SHIFT)) - (item->pos.x_pos + (object->pivot_length * phd_sin(item->pos.y_rot) >> W2V_SHIFT));
	}
	else
	{
		z = (enemy->pos.z_pos + (enemy->speed * PREDICTIVE_SCALE_FACTOR * phd_cos(enemy->pos.y_rot) >> W2V_SHIFT)) - (item->pos.z_pos + (object->pivot_length * phd_cos(item->pos.y_rot) >> W2V_SHIFT));
		x = (enemy->pos.x_pos + (enemy->speed * PREDICTIVE_SCALE_FACTOR * phd_sin(enemy->pos.y_rot) >> W2V_SHIFT)) - (item->pos.x_pos + (object->pivot_length * phd_sin(item->pos.y_rot) >> W2V_SHIFT));
	}

  	y = item->pos.y_pos - enemy->pos.y_pos;
	angle = phd_atan(z, x);

	if (z>32000 || z<-32000 || x>32000 || x<-32000)	// Well out of range.
		info->distance = 0x7fffffff;
	else
	{
		if (creature->enemy)
			info->distance = z*z + x*x;
		else
			info->distance = 0x7fffffff;
	}

	info->angle = angle - item->pos.y_rot;
	info->enemy_facing = 0x8000 + angle - enemy->pos.y_rot;

	// x_angle is the angle based on difference in heights
	z = abs(z);
	x = abs(x);

	// frig to make lara smaller if she's ducking etc.
	if (enemy == lara_item)
	{
		lara_anim = lara_item->current_anim_state;
		if (lara_anim == AS_DUCK || lara_anim == AS_DUCKROLL || lara_anim ==AS_ALL4S || lara_anim == AS_CRAWL ||
			lara_anim == AS_ALL4TURNL || lara_anim == AS_ALL4TURNR)
		{
			y -= (STEP_L*3/2);
		}
	}

	// don't need exact distance
	if (x > z)
		info->x_angle = phd_atan(x + (z >> 1), y);
	else
		info->x_angle = phd_atan(z + (x >> 1), y);

	// should there be a limit on x_angle to stop things bending too far?

	/* If enemy is in front arc this effects a lot of the baddies choices */
	info->ahead = (info->angle > -FRONT_ARC && info->angle < FRONT_ARC);
//	info->bite = (info->ahead && enemy->hit_points>0 && ABS(enemy->pos.y_pos - item->pos.y_pos) <= STEP_L*3/2);
	info->bite = (info->ahead && enemy->hit_points>0 && ABS(enemy->pos.y_pos - item->pos.y_pos) <= STEP_L*2);
}


static int SearchLOT(LOT_INFO *LOT, int expansion)
{
/* Expand a certain number of nodes on the LOT table to help find routes.
	Returns 0 if nothing more to expand, 1 otherwise */
	int i, index, done, change, box_number;
	BOX_NODE *node, *expand;
	BOX_INFO *box;
	sint16 *zone, search_zone;

	if (LOT->fly == NO_FLYING)
	{
		zone = ground_zone[ZONE(LOT->step)][flip_status];
		search_zone = zone[LOT->head];
	}
	else
	{
		zone = ground_zone[ZONE(1)][0];	//Shouldn't ever need this, but just to prevent NULL pointer worries
		search_zone = FLY_ZONE;
	}


//#define LOT_LOG
#ifdef LOT_LOG
	Log("START%d: Zone%d (head%d tail%d)\n", flip_status, search_zone, LOT->head, LOT->tail);
#endif
	for (i=0; i<expansion; i++)
	{
		/* Expand head node */
		if (LOT->head == NO_BOX)
		{
#ifdef LOT_LOG
			Log("<EMPTY>");
#endif
			LOT->tail = NO_BOX; // 16/9/97: clear tail
			return (0);
		}

		node = &LOT->node[LOT->head];
		box = &boxes[LOT->head];
#ifdef LOT_LOG
		Log("%d Expand%d(%d,%d,%d,%d:%d):", node->search_number & SEARCH_NUMBER, LOT->head, box->left, box->top, box->right, box->bottom, box->height);
#endif

		/* Expand node */
		index = box->overlap_index & OVERLAP_INDEX;
		done = 0;
		do
		{
			box_number = overlap[index++];
			if (box_number & BOX_END_BIT)
			{
				done = 1;
				box_number &= BOX_NUMBER;
			}

			/* Allow for zone considerations */
			if (LOT->fly == NO_FLYING && search_zone != zone[box_number])
				continue;

			/* Allow for LOT consideration */
			change = boxes[box_number].height - box->height;
			if (change > LOT->step || change < LOT->drop)
				continue;

			/* Node may already have been expanded recently */
			expand = &LOT->node[box_number];
#ifdef LOT_LOG
			Log(" (%d %d=%d)", box_number, node->search_number & SEARCH_NUMBER, expand->search_number & SEARCH_NUMBER);
#endif
			if ((node->search_number & SEARCH_NUMBER) < (expand->search_number & SEARCH_NUMBER))
				continue;

			if (node->search_number & BLOCKED_SEARCH)
			{
				/* If equal search number then don't expand */
				if ((node->search_number & SEARCH_NUMBER) == (expand->search_number & SEARCH_NUMBER))
					continue;
#ifdef LOT_LOG
				Log(" BS");
#endif
				expand->search_number = node->search_number;
			}
			else
			{
				/* If equal search number then only expand if 'expand' is currently a BLOCKED_SEARCH */
				if ((node->search_number & SEARCH_NUMBER) == (expand->search_number & SEARCH_NUMBER) &&
					!(expand->search_number & BLOCKED_SEARCH))
					continue;

				/* Is this a blocked box? If so we're into a BLOCKED_SEARCH */
				if (boxes[box_number].overlap_index & LOT->block_mask)
				{
					expand->search_number = node->search_number | BLOCKED_SEARCH;
#ifdef LOT_LOG
					Log(" NB");
#endif
				}
				else
				{
					expand->search_number = node->search_number;
					expand->exit_box = LOT->head;
				}
			}

			/* Don't add to tail of list if already there!! */
			if (expand->next_expansion == NO_BOX && box_number != LOT->tail)
			{
				/* Add node to tail of node list */
				LOT->node[LOT->tail].next_expansion = box_number;
				LOT->tail = box_number;
#ifdef LOT_LOG
				Log(" %d", box_number);
#endif
			}
#ifdef LOT_LOG
			else Log(" %d on list", box_number);
#endif
		}
		while (!done);

		/* Go onto next node */
		LOT->head = node->next_expansion;
		node->next_expansion = NO_BOX;
	}

	return (1);
}


int UpdateLOT(LOT_INFO *LOT, int expansion)
{
/* Expands LOT list by up to 'expansion' nodes, allowing for restarts caused the
	target moving. Returns 1 if more expansion is needed, 0 otherwise */
	BOX_NODE *expand;

	/* If target has passed into new box, add this to head of expansion list and update
	search number (only 65536 searches possible, so things will go wrong if
	player passes through this many boxes during the course of a level) */
	if (LOT->required_box != NO_BOX && LOT->required_box != LOT->target_box)
	{
		LOT->target_box = LOT->required_box;

		expand = &LOT->node[LOT->target_box];
		if (expand->next_expansion == NO_BOX && LOT->tail != LOT->target_box)
		{
			expand->next_expansion = LOT->head;

			/* List may be empty */
			if (LOT->head == NO_BOX)
				LOT->tail = LOT->target_box;

			LOT->head = LOT->target_box;
		}

		expand->search_number = ++LOT->search_number;
		expand->exit_box = NO_BOX;
#ifdef LOT_LOG
		Log("Search(%d) for box %d (H%d,T%d n%d)", LOT->search_number, LOT->target_box, LOT->head, LOT->tail, expand->next_expansion);
#endif
	}

	/* Expand LOT */
	return (SearchLOT(LOT, expansion));
}


/******************************************************************/
/*                                                                */
/* All the stuff needed to determine a suitable target for baddie */
/*                                                                */
/******************************************************************/

void TargetBox(LOT_INFO *LOT, sint16 box_number)
{
	/* Set target to random position in box */
	BOX_INFO *box;

	box_number &= BOX_NUMBER;

	box = &boxes[box_number];

	LOT->target.z = (((uint32)box->left << WALL_SHIFT)) + GetRandomControl() * (((uint32)box->right - (uint32)box->left - 1) >> (15-WALL_SHIFT)) + WALL_L/2;
	LOT->target.x = ( (uint32)box->top << WALL_SHIFT ) + GetRandomControl() * (( (uint32)box->bottom - (uint32)box->top - 1 ) >> (15-WALL_SHIFT)) + WALL_L/2;
	LOT->required_box = box_number;

	if (LOT->fly == NO_FLYING)
		LOT->target.y = box->height;
	else
		LOT->target.y = box->height - STEP_L*3/2;
}


#define ESCAPE_DIST (WALL_L*5)

int EscapeBox(ITEM_INFO *item, ITEM_INFO *enemy, sint16 box_number)
{
	/* Valid box cannot be in opposite quad to baddie */
	BOX_INFO *box;
	sint32 x, z;

	box = &boxes[box_number];
	z = (( (uint32)box->left + (uint32)box->right ) << (WALL_SHIFT-1)) - enemy->pos.z_pos;
	x = (( (uint32)box->top + (uint32)box->bottom ) << (WALL_SHIFT-1)) - enemy->pos.x_pos;

	/* Is box far enough from enemy to be escape type? */
	if (x > -ESCAPE_DIST && x < ESCAPE_DIST && z > -ESCAPE_DIST && z < ESCAPE_DIST)
		return (0);

	/* Don't go to opposite quad to one we're in (i.e. via enemy) */
	if (((z>0) ^ (item->pos.z_pos > enemy->pos.z_pos)) &&
		((x>0) ^ (item->pos.x_pos > enemy->pos.x_pos)))
		return (0);

	return (1);
}


int ValidBox(ITEM_INFO *item, sint16 zone_number, sint16 box_number)
{
	/* Valid box is one in same zone (not blocked), but one that item is not currently in */
	BOX_INFO *box;
	CREATURE_INFO *creature;
	sint16 *zone;

	creature = (CREATURE_INFO *)item->data;
	if (creature->LOT.fly == NO_FLYING)
		zone = ground_zone[ZONE(creature->LOT.step)][flip_status];
	else
		zone = ground_zone[ZONE(1)][0];	//Shouldn't ever need this, but just to prevent NULL pointer worries

	/* Zone may be BLOCKED during LOT search */
	if (creature->LOT.fly == NO_FLYING && zone[box_number] != zone_number)
		return (0);

	box = &boxes[box_number];
	if (box->overlap_index & creature->LOT.block_mask)
		return (0);

	if (item->pos.z_pos > ((sint32)box->left << WALL_SHIFT) && item->pos.z_pos < ((sint32)box->right << WALL_SHIFT) &&
		item->pos.x_pos > ((sint32)box->top << WALL_SHIFT) && item->pos.x_pos < ((sint32)box->bottom << WALL_SHIFT))
		return (0);

	return (1);
}


#define ATTACK_RANGE SQUARE(WALL_L*3)
#define ESCAPE_CHANCE  0x800
#define RECOVER_CHANCE 0x100

void GetCreatureMood(ITEM_INFO *item, AI_INFO *info, int violent)
{
	/* Use creatures position and status to decide its mood, and thus its target */
	CREATURE_INFO *creature;
	MOOD_TYPE mood;
	LOT_INFO *LOT;
	ITEM_INFO *enemy;

	if (item->data == NULL)
		return;

	creature = (CREATURE_INFO *)item->data;
	enemy = creature->enemy;
	LOT = &creature->LOT;
//#define LOT_LOG
#ifdef LOT_LOG
	Log("Mood%d target%d box%d", creature->mood, LOT->target_box, item->box_number);
#endif


	/* Clear required box if BLOCKED */
	if (LOT->node[item->box_number].search_number == (LOT->search_number | BLOCKED_SEARCH))
		LOT->required_box = NO_BOX;

	if (creature->mood != ATTACK_MOOD && LOT->required_box != NO_BOX)
	{
		/* If within box target, clear required box and become BORED */
		if (!ValidBox(item, info->zone_number, LOT->target_box))
		{
			/* Creatures will run away until you enter their zone */
			if (info->zone_number == info->enemy_zone)
				creature->mood = BORED_MOOD;
			LOT->required_box = NO_BOX;
		}
	}

	/* Remember current mood to see if any mood swing occurs */
	mood = creature->mood;

	/* First decide mood */
	if (!enemy)
	{
		creature->mood = BORED_MOOD;
		enemy = lara_item;
	}
//	else if (!enemy->active)
	else if (enemy->hit_points <= 0 && enemy == lara_item)	// Tom - watch out for this one!
		creature->mood = BORED_MOOD;
	else if (violent)
	{
	/* VIOLENT creature mood. Basically ATTACK if enemy in same zone, STALK if not, and
		only run away if she shoots while cannot ATTACK */
		switch (creature->mood)
		{
		case ATTACK_MOOD:
			/* If enemy leaves zone, become BORED */
			if (info->zone_number != info->enemy_zone)
				creature->mood = BORED_MOOD;
			break;

		case BORED_MOOD:
		case STALK_MOOD:
			/* If same zone, ATTACK. If not in same zone and shot at, ESCAPE */
			if (info->zone_number == info->enemy_zone)
				creature->mood = ATTACK_MOOD;
			else if (item->hit_status)
				creature->mood = ESCAPE_MOOD;
			break;

		case ESCAPE_MOOD:
			/* If enemy is in same zone ATTACK */
			if (info->zone_number == info->enemy_zone)
				creature->mood = ATTACK_MOOD;
			break;
		}
	}
	else
	{
		/* TIMID creature mood. Basically, run away if shot at, and STALK to attack position */
		switch (creature->mood) //Changed by TS 24/8/98
		{
		case ATTACK_MOOD:
			/* If enemy shoots ESCAPE. If enemy leaves zone, become BORED */
			if (item->hit_status && (GetRandomControl() < ESCAPE_CHANCE || info->zone_number != info->enemy_zone))
				creature->mood = STALK_MOOD; // TS - changed ESCAPE to STALK
			else if (info->zone_number != info->enemy_zone && info->distance > (WALL_L*6))
				creature->mood = BORED_MOOD;
			break;

		case BORED_MOOD:
		case STALK_MOOD:
			// If shot by enemy, ESCAPE. Else if same zone as enemy and close enough, ATTACK
			if (creature->alerted && info->zone_number != info->enemy_zone)
			{
				if (info->distance > (WALL_L*3))
					creature->mood = STALK_MOOD;
				else
					creature->mood = BORED_MOOD;
			}
			else if (info->zone_number == info->enemy_zone)
			{
				if (info->distance < ATTACK_RANGE || (creature->mood == STALK_MOOD && LOT->required_box == NO_BOX))
					creature->mood = ATTACK_MOOD;
				else
					creature->mood = STALK_MOOD;
			}
			break;

		case ESCAPE_MOOD:
			/* May recover in ESCAPE_MOOD target box selection if can find suitable STALK */
			if (info->zone_number == info->enemy_zone && GetRandomControl() < RECOVER_CHANCE)
				creature->mood = STALK_MOOD;
			break;
		}
	}


	/* Any change of mood results in required box being undefined */
	if (mood != creature->mood)
	{
	/* Change from ATTACK mood means that need to retarget away from Lara's old position (which may be
		unreachable if right near the edge of a block */
		if (mood == ATTACK_MOOD)
			TargetBox(LOT, LOT->target_box);

		LOT->required_box = NO_BOX;
	}
}

void CreatureMood(ITEM_INFO *item, AI_INFO *info, int violent)
{
	/* Use creatures position and status to decide its mood, and thus its target */
	sint16 box_number;
	CREATURE_INFO *creature;
	LOT_INFO *LOT;
	OBJECT_INFO *object;
	ITEM_INFO *enemy;
	sint16 *bounds;

	if (item->data == NULL)
		return;

	creature = (CREATURE_INFO *)item->data;
	enemy = creature->enemy;
	LOT = &creature->LOT;
//#define LOT_LOG

	/* Selected mood, so now select target (which may change mood anyway) */
	switch (creature->mood)
	{
	case ATTACK_MOOD:
#ifdef LOT_LOG
		Log("ATTACK");
#endif
		/* Enemy position is target */
		LOT->target.x = enemy->pos.x_pos;
		LOT->target.y = enemy->pos.y_pos;
		LOT->target.z = enemy->pos.z_pos;
		LOT->required_box = enemy->box_number;
		/* Target for flying creatures */
		if (LOT->fly != NO_FLYING)
		{
			object = &objects[enemy->object_number];
			if (lara.water_status == LARA_ABOVEWATER)
			{
				bounds = GetBestFrame(enemy);
				LOT->target.y += bounds[2];
			}
		}
		break;

	case BORED_MOOD:
#ifdef LOT_LOG
		Log("BORED");
#endif
		/* Get a random box and see if same zone; if so, only except it if suitable
		for STALK mode, or need a new target */
		box_number = LOT->node[GetRandomControl() * LOT->zone_count >> 15].box_number;
		if (ValidBox(item, info->zone_number, box_number))
		{
			if (StalkBox(item, enemy, box_number) && enemy->hit_points > 0 && creature->enemy)
			{
				TargetBox(LOT, box_number);
				creature->mood = BORED_MOOD;
			}
			else if (LOT->required_box == NO_BOX)
				TargetBox(LOT, box_number);
		}
		break;

	case STALK_MOOD:
#ifdef LOT_LOG
		Log("STALK");
#endif
		/* Only get a new box if need one or current stalk box is no longer really valid.
		In former case, change to BORED if find box not suitable for stalking. */
		if (LOT->required_box == NO_BOX || !StalkBox(item, enemy, LOT->required_box))
		{
			box_number = LOT->node[GetRandomControl() * LOT->zone_count >> 15].box_number;
			if (ValidBox(item, info->zone_number, box_number))
			{
				if (StalkBox(item, enemy, box_number))
					TargetBox(LOT, box_number);
				else if (LOT->required_box == NO_BOX)
				{
					/* If not a valid stalk box, only take it on if no current stalk box at all */
					TargetBox(LOT, box_number);

					/* Become bored if enemy isn't in same region anyway */
					if (info->zone_number != info->enemy_zone)
						creature->mood = BORED_MOOD;
				}
			}
		}
		break;

	case ESCAPE_MOOD:
#ifdef LOT_LOG
		Log("ESCAPE");
#endif
		box_number = LOT->node[GetRandomControl() * LOT->zone_count >> 15].box_number;
		if (ValidBox(item, info->zone_number, box_number))
		{
			/* If waiting for a valid box, and this is it, use it */
			if (LOT->required_box == NO_BOX)
			{
				if (EscapeBox(item, enemy, box_number))
					TargetBox(LOT, box_number);
				else if (info->zone_number == info->enemy_zone && StalkBox(item, enemy, box_number) && !violent)
				{
				/* If TIMID and running away while enemy in same zone, then switch to STALK
					when get happy */
					TargetBox(LOT, box_number);
					creature->mood = STALK_MOOD;
				}
			}
		}
		break;
	}

	if (LOT->target_box == NO_BOX)
		TargetBox(LOT, item->box_number);

	/* Got target based on mood, now calculate intermediate target to get there */

	CalculateTarget(&creature->target, item, &creature->LOT);

}

TARGET_TYPE CalculateTarget(PHD_VECTOR *target, ITEM_INFO *item, LOT_INFO *LOT)
{
/* Required target is specified in LOT structure - this function uses LOT
 * search tree	to calculate a suitable intermediate target.
 * Returns 0 if reaches point where trail runs dry, 1 if primary target near,
 * 2 if intermediate target
 */
	int box_number, prime_free;
	BOX_INFO *box;
	sint32 left, right, top, bottom;
	sint32 box_left, box_right, box_top, box_bottom;


	UpdateLOT(LOT, MAX_EXPANSION);

/* Do search through LOT to find best PRIMARY or SECONDARY target to aim for */
	target->x = item->pos.x_pos;
	target->y = item->pos.y_pos;
	target->z = item->pos.z_pos;

	box_number = item->box_number;
	if (box_number == NO_BOX)
		return (NO_TARGET);

/* Initialise to stop GNU warning (first box will ALWAYS initialise them to its bounds) */
// WRONG - they can end up uninitialsed - so added the next few lines - TS
	box = &boxes[box_number];

	box_left = (sint32)box->left << WALL_SHIFT;
	box_right = ((sint32)box->right << WALL_SHIFT)-1;
	box_top = (sint32)box->top << WALL_SHIFT;
	box_bottom = ((sint32)box->bottom << WALL_SHIFT)-1;

	left = box_left;
	right = box_right;
	top = box_top;
	bottom = box_bottom;

// end of added lines
//	left = right = top = bottom = 0; //This is WRONG

	prime_free = ALL_CLIP;

	do {
	/* If already in this box, adjust clip box */
		box = &boxes[box_number];

	/* Vertical y pos for flying things */
		if (LOT->fly == NO_FLYING)
		{
			if (target->y > box->height)
				target->y = box->height;
		}
		else
		{
			if (target->y > box->height - WALL_L)
				target->y = box->height - WALL_L;
		}

		box_left = (sint32)box->left << WALL_SHIFT;
		box_right = ((sint32)box->right << WALL_SHIFT)-1;
		box_top = (sint32)box->top << WALL_SHIFT;
		box_bottom = ((sint32)box->bottom << WALL_SHIFT)-1;

		if (item->pos.z_pos >= box_left && item->pos.z_pos <= box_right &&
			item->pos.x_pos >= box_top && item->pos.x_pos <= box_bottom)
		{
			left = box_left;
			right = box_right;
			top = box_top;
			bottom = box_bottom;
		}
		else
		{
			if (item->pos.z_pos < box_left)
			{
			/* Primary or secondary clip? */
				if ((prime_free & CLIP_LEFT) && item->pos.x_pos >= box_top && item->pos.x_pos <= box_bottom)
				{
				/* Primary clip */
					if (target->z < box_left + BIFF)
						target->z = box_left + BIFF;

					if (prime_free & SECONDARY_CLIP)
						return (SECONDARY_TARGET);

					if (box_top > top)
						top = box_top;
					if (box_bottom < bottom)
						bottom = box_bottom;

					prime_free = CLIP_LEFT;
				}
				else if (prime_free != CLIP_LEFT)
				{
				/* Secondary clip */
					target->z = right - BIFF;
					if (prime_free != ALL_CLIP)
						return (SECONDARY_TARGET);

					prime_free |= SECONDARY_CLIP;
				}
			}
			else if (item->pos.z_pos > box_right)
			{
			/* Primary or secondary clip? */
				if ((prime_free & CLIP_RIGHT) && item->pos.x_pos >= box_top && item->pos.x_pos <= box_bottom)
				{
				/* Primary clip */
					if (target->z > box_right - BIFF)
						target->z = box_right - BIFF;

					if (prime_free & SECONDARY_CLIP)
						return (SECONDARY_TARGET);

					if (box_top > top)
						top = box_top;
					if (box_bottom < bottom)
						bottom = box_bottom;

					prime_free = CLIP_RIGHT;
				}
				else if (prime_free != CLIP_RIGHT)
				{
				/* Secondary clip */
					target->z = left + BIFF;
					if (prime_free != ALL_CLIP)
						return (SECONDARY_TARGET);

					prime_free |= SECONDARY_CLIP;
				}
			}

			if (item->pos.x_pos < box_top)
			{
			/* Primary or secondary clip? */
				if ((prime_free & CLIP_TOP)  && item->pos.z_pos >= box_left && item->pos.z_pos <= box_right)
				{
				/* Primary clip */
					if (target->x < box_top + BIFF)
						target->x = box_top + BIFF;

					if (prime_free & SECONDARY_CLIP)
						return (SECONDARY_TARGET);

					if (box_left > left)
						left = box_left;
					if (box_right < right)
						right = box_right;

					prime_free = CLIP_TOP;
				}
				else if (prime_free != CLIP_TOP)
				{
				/* Secondary clip */
					target->x = bottom - BIFF;
					if (prime_free != ALL_CLIP)
						return (SECONDARY_TARGET);

					prime_free |= SECONDARY_CLIP;
				}
			}
			else if (item->pos.x_pos > box_bottom)
			{
			/* Primary or secondary clip? */
				if ((prime_free & CLIP_BOTTOM) && item->pos.z_pos >= box_left && item->pos.z_pos <= box_right)
				{
				/* Primary clip */
					if (target->x > box_bottom - BIFF)
						target->x = box_bottom - BIFF;

					if (prime_free & SECONDARY_CLIP)
						return (SECONDARY_TARGET);

					if (box_left > left)
						left = box_left;
					if (box_right < right)
						right = box_right;

					prime_free = CLIP_BOTTOM;
				}
				else if (prime_free != CLIP_BOTTOM)
				{
				/* Secondary clip */
					target->x = top + BIFF;
					if (prime_free != ALL_CLIP)
						return (SECONDARY_TARGET);

					prime_free |= SECONDARY_CLIP;
				}
			}
		}

	/* May be in primary target box */
		if (box_number == LOT->target_box)
		{
			if (prime_free & (CLIP_LEFT|CLIP_RIGHT))
				target->z = LOT->target.z;
			else if (!(prime_free & SECONDARY_CLIP))
			{
				if (target->z < box_left + BIFF)
					target->z = box_left + BIFF;
				else if (target->z > box_right - BIFF)
					target->z = box_right - BIFF;
			}

			if (prime_free & (CLIP_TOP|CLIP_BOTTOM))
				target->x = LOT->target.x;
			else if (!(prime_free & SECONDARY_CLIP))
			{
				if (target->x < box_top + BIFF)
					target->x = box_top + BIFF;
				else if (target->x > box_bottom - BIFF)
					target->x = box_bottom - BIFF;
			}

			target->y = LOT->target.y;

			return (PRIME_TARGET);
		}

	/* Get exit box */
		box_number = LOT->node[box_number].exit_box;
		if (box_number != NO_BOX && (boxes[box_number].overlap_index & LOT->block_mask))
			break;
	}
	while (box_number != NO_BOX);


/* No valid target! Better use centre of last box reached (should only occur due to BLOCKAGE problems) */
	if (prime_free & (CLIP_LEFT|CLIP_RIGHT))
		target->z = box_left + WALL_L/2 + (GetRandomControl() * (box_right - box_left - WALL_L) >> 15);
	else if (!(prime_free & SECONDARY_CLIP))
	{
		if (target->z < box_left + BIFF)
			target->z = box_left + BIFF;
		else if (target->z > box_right - BIFF)
			target->z = box_right - BIFF;
	}

	if (prime_free & (CLIP_TOP|CLIP_BOTTOM))
		target->x = box_top + WALL_L/2 + (GetRandomControl() * (box_bottom - box_top - WALL_L) >> 15);
	else if (!(prime_free & SECONDARY_CLIP))
	{
		if (target->x < box_top + BIFF)
			target->x = box_top + BIFF;
		else if (target->x > box_bottom - BIFF)
			target->x = box_bottom - BIFF;
	}

	if (LOT->fly == NO_FLYING)
		target->y = box->height;
	else
		target->y = box->height - STEP_L*3/2;

	return (NO_TARGET);
}


int CreatureCreature(sint16 item_number)
{
	/* Check out all other baddies in room for overlapping bounds */
	// TS - 27/7/98 - changed to return angle of turn to avoid baddy rather than 1/0
	sint16 link, angle;
	sint32 x, y, z, y_rot, xdistance, zdistance, distance, radius;
	ROOM_INFO *r;
	ITEM_INFO *item;

	item = &items[item_number];
	x = item->pos.x_pos;
	y = item->pos.y_pos;
	z = item->pos.z_pos;
	y_rot = item->pos.y_rot;
	// radius = SQUARE(objects[item->object_number].radius);
	radius = objects[item->object_number].radius;

	r = &room[item->room_number];
	link = r->item_number;
	do
	{
		item = &items[link];

		/* End the moment find this item (sort of priority) */
		if (link == item_number)
			return (0);

		/* Don't stop just cuz Lara in the way */
		if (item != lara_item && item->status==ACTIVE && item->hit_points > 0)//  /* && item->speed!=0*/
		{
	//	distance = SQUARE(item->pos.x_pos - x) + SQUARE(item->pos.y_pos - y) + SQUARE(item->pos.z_pos - z);
	// changed by TS - not sure if it works yet
			xdistance = abs(item->pos.x_pos - x);
			zdistance = abs(item->pos.z_pos - z);
			if (xdistance > zdistance)
				distance = xdistance + (zdistance >> 1);
			else
				distance = zdistance + (xdistance >> 1);
		//	if (distance < radius)
			if (distance < radius + objects[item->object_number].radius)
			{
				angle = phd_atan(item->pos.z_pos - z, item->pos.x_pos - x) - y_rot;
				return (angle);
			}
		}

		link = item->next_item;
	}
	while (link != NO_ITEM);

	return (0);
}


int BadFloor(sint32 x, sint32 y, sint32 z, sint32 box_height, sint32 next_height, sint16 room_number, LOT_INFO *LOT)
{
	/* Check out whether floor position provides an obstruction by using boxes */
	sint32 height;
	FLOOR_INFO *floor;

	/* Get adjacent box */
	floor = GetFloor(x, y, z, &room_number);
	if (floor->box == NO_BOX)
		return (1);

	if (boxes[floor->box].overlap_index & LOT->block_mask)
		return (1);

	height = boxes[floor->box].height;
	if (box_height - height > LOT->step || box_height - height < LOT->drop)
		return (1);

	/* Don't allow creature to jump down a height lower than its next LOT box (else it will
	fall off every ledge rather than keep to the path that leads where it wants to go) */
	if (box_height - height < -LOT->step && height > next_height)
		return (1);

	/* Flying creatures need to check their y coord against this too */
	if ((LOT->fly != NO_FLYING) && y > height + LOT->fly)
		return (1);



	/*
	GetHeight(floor, x, y, z);
	if (height_type == 3)
		return (1);
	*/


	return (0);
}


void CreatureDie(sint16 item_number, int explode)
{
	sint16 pickup_number, room_number;
	ITEM_INFO *item, *pickup;
	FLOOR_INFO *floor;

	item = &items[item_number];

	item->collidable = 0;
	item->hit_points = DONT_TARGET;
	if (explode)
	{
		ExplodingDeath(item_number, 0xffffffff, 0);
		KillItem(item_number);
	}
	else
		RemoveActiveItem(item_number);
	DisableBaddieAI(item_number);
	item->flags |= ONESHOT; // 30/7/97: don't want bad guys triggered ever again

	/* Add clearable bodies to the body bag */
	if (item->clear_body)
	{
		item->next_active = body_bag;
		body_bag = item_number;
	}

	// drop any carried items
	pickup_number = item->carried_item;

	while (pickup_number != NO_ITEM)
	{
		pickup = &items[pickup_number];
		pickup->pos.x_pos = (item->pos.x_pos & ~1023) | 512;
		pickup->pos.z_pos = (item->pos.z_pos & ~1023) | 512;
		room_number = item->room_number;
		floor = GetFloor(pickup->pos.x_pos, item->pos.y_pos, pickup->pos.z_pos, &room_number);
//		if (item->pos.y_pos > item->floor)
			pickup->pos.y_pos = GetHeight(floor, pickup->pos.x_pos, item->pos.y_pos, pickup->pos.z_pos);
//		else
//			pickup->pos.y_pos = item->floor;

		ItemNewRoom(pickup_number, item->room_number);

		pickup_number = pickup->carried_item;
	}

}


int CreatureAnimation(sint16 item_number, sint16 angle, sint16 tilt)
{
	/* Final thing that beastie control routines should call */
	sint32 radius, shift_x, shift_z, top;
	sint32 height, box_height, next_height, ceiling;
	sint32 x, y, z, pos_x, pos_z, dy;
	PHD_VECTOR old;
	ITEM_INFO *item;
	CREATURE_INFO *creature;
	LOT_INFO *LOT;
	FLOOR_INFO *floor;
	sint16 *zone, *bounds;
	sint16 room_number;
	sint16 next_box;
	sint16 biff_angle;

	item = &items[item_number];
	if (item->data == NULL)
		return (0);

	creature = (CREATURE_INFO *)item->data;
	LOT = &creature->LOT;


	/* Store creature's current position */
	old.x = item->pos.x_pos;
	old.y = item->pos.y_pos;
	old.z = item->pos.z_pos;
	#ifdef DEBUG_BOX

//	sprintf(exit_message, "Target Box:%d, My Box:%d, Lara's Box:%d", LOT->target_box, item->box_number, lara_item->box_number);
//	PrintDbug(2, 1, exit_message);
//	sprintf(exit_message, "Target X:%d, Target Z:%d, Angle:%d", creature->target.x, creature->target.z, angle);
//	PrintDbug(2,2, exit_message);

#endif


	/* Need old box height and zone */
	box_height = boxes[item->box_number].height;
	if (LOT->fly == NO_FLYING)
		zone = ground_zone[ZONE(LOT->step)][flip_status];
	else
		zone = ground_zone[ZONE(1)][0]; //Just to be on the safe side

	/* For non-swimming baddies, need to adjust room to one by feet for SFX */
	if (!objects[item->object_number].water_creature)
	{
		room_number = item->room_number;
		GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
		if (room_number != item->room_number)
			ItemNewRoom(item_number, room_number);
	}

	AnimateItem(item);
	if ( item->status==DEACTIVATED )
	{
		CreatureDie(item_number, 0);
		return 0;
	}

	bounds = GetBoundsAccurate(item);
	y = item->pos.y_pos + bounds[2];

	/* Do centre point test for collisions against walls */
	room_number = item->room_number;
	GetFloor(old.x, y, old.z, &room_number); // 29/6/97: added so go up/down rooms correctly
	floor = GetFloor(item->pos.x_pos, y, item->pos.z_pos, &room_number);
	height = boxes[floor->box].height;
	// TS - this is one of the places where real LOT is used in CreatureAnimation
	// Need to find an alternative for non-LOT creatures
	if (!objects[item->object_number].non_lot)
  		next_box = LOT->node[floor->box].exit_box;
	else
	{
//		floor = GetFloor(item->pos.x_pos, y, creature->target.z, &room_number);
		floor = GetFloor(item->pos.x_pos,item->pos.y_pos, item->pos.z_pos, &room_number);
		height = boxes[floor->box].height;
		next_box = floor->box;
	}

	if (next_box != NO_BOX)
		next_height = boxes[next_box].height;
	else
		next_height = height;

	/* First clamp positions that are illegal back to legal (unusual) */
	if (floor->box == NO_BOX || (LOT->fly == NO_FLYING && zone[item->box_number] != zone[floor->box]) ||
		box_height - height > LOT->step || box_height - height < LOT->drop)
	{
//Log("%d(b%d) %d(b%d)", zone[item->box_number], item->box_number, zone[floor->box], floor->box);

		pos_x = item->pos.x_pos >> WALL_SHIFT;
		pos_z = item->pos.z_pos >> WALL_SHIFT;

		shift_x = old.x >> WALL_SHIFT;
		shift_z = old.z >> WALL_SHIFT;

		if (pos_x < shift_x)
			item->pos.x_pos = old.x & (~(WALL_L-1));
		else if (pos_x > shift_x)
			item->pos.x_pos = old.x | (WALL_L-1);

		if (pos_x < shift_z)
			item->pos.z_pos = old.z & (~(WALL_L-1));
		else if (pos_x > shift_z)
			item->pos.z_pos = old.z | (WALL_L-1);

		floor = GetFloor(item->pos.x_pos, y, item->pos.z_pos, &room_number);
		height = boxes[floor->box].height;
	// TS - this is one of the places where real LOT is used in CreatureAnimation
	// Need to find an alternative for non-LOT creatures
		if (!objects[item->object_number].non_lot)
			next_box = LOT->node[floor->box].exit_box;
		else
		{
//			floor = GetFloor(creature->target.x, y, creature->target.z, &room_number);
			floor = GetFloor(item->pos.x_pos,item->pos.y_pos, item->pos.z_pos, &room_number);
			height = boxes[floor->box].height;
			next_box = floor->box;
		}

		if (next_box != NO_BOX)
			next_height = boxes[next_box].height;
		else
			next_height = height;
	}

	x = item->pos.x_pos;
	z = item->pos.z_pos;

	pos_x = x & (WALL_L-1);
	pos_z = z & (WALL_L-1);
	radius = objects[item->object_number].radius;

	shift_x = shift_z = 0;

	/* Check all the possible grid collision points (including diagonals) and get
	suitable shifts to move away from bad places */
	if (pos_z < radius)
	{
		/* Test z first */
		if (BadFloor(x, y, z-radius, height, next_height, room_number, LOT))
			shift_z = radius - pos_z;

		if (pos_x < radius)
		{
			/* Test x second: if that fails, and no z shift, then (and only then) test the diagonal */
			if (BadFloor(x-radius, y, z, height, next_height, room_number, LOT))
				shift_x = radius - pos_x;
			else if (!shift_z && BadFloor(x-radius, y, z-radius, height, next_height, room_number, LOT))
			{
				/* Diagonals: choose which shift based on direction of travel (both shifts leads to sticking) */
				if (item->pos.y_rot > -0x6000 && item->pos.y_rot < 0x2000)
					shift_z = radius - pos_z;
				else
					shift_x = radius - pos_x;
			}
		}
		else if (pos_x > WALL_L-radius)
		{
			if (BadFloor(x+radius, y, z, height, next_height, room_number, LOT))
				shift_x = WALL_L-radius - pos_x;
			else if (!shift_z && BadFloor(x+radius, y, z-radius, height, next_height, room_number, LOT))
			{
				if (item->pos.y_rot > -0x2000 && item->pos.y_rot < 0x6000)
					shift_z = radius - pos_z;
				else
					shift_x = WALL_L-radius - pos_x;
			}
		}
	}
	else if (pos_z > WALL_L-radius)
	{
		if (BadFloor(x, y, z+radius, height, next_height, room_number, LOT))
			shift_z = WALL_L-radius - pos_z;

		if (pos_x < radius)
		{
			if (BadFloor(x-radius, y, z, height, next_height, room_number, LOT))
				shift_x = radius - pos_x;
			else if (!shift_z && BadFloor(x-radius, y, z+radius, height, next_height, room_number, LOT))
			{
				if (item->pos.y_rot > -0x2000 && item->pos.y_rot < 0x6000)
					shift_x = radius - pos_x;
				else
					shift_z = WALL_L-radius - pos_z;
			}
		}
		else if (pos_x > WALL_L-radius)
		{
			if (BadFloor(x+radius, y, z, height, next_height, room_number, LOT))
				shift_x = WALL_L-radius - pos_x;
			else if (!shift_z && BadFloor(x+radius, y, z+radius, height, next_height, room_number, LOT))
			{
				if (item->pos.y_rot > -0x6000 && item->pos.y_rot < 0x2000)
					shift_x = WALL_L-radius - pos_x;
				else
					shift_z = WALL_L-radius - pos_z;
			}
		}
	}
	else if (pos_x < radius)
	{
		if (BadFloor(x-radius, y, z, height, next_height, room_number, LOT))
			shift_x = radius - pos_x;
	}
	else if (pos_x > WALL_L-radius)
	{
		if (BadFloor(x+radius, y, z, height, next_height, room_number, LOT))
			shift_x = WALL_L-radius - pos_x;
	}

	/* Update information */
	item->pos.x_pos += shift_x;
	item->pos.z_pos += shift_z;

//
// New code by Gibby to stop enemies going on steep triangles
//
//	floor = GetFloor(item->pos.x_pos, y, item->pos.z_pos, &room_number);
//	GetHeight(floor, item->pos.x_pos, y, item->pos.z_pos);
//
//	if (height_type == DIAGONAL)
//	{
//		item->pos.x_pos = old.x;
//		item->pos.y_pos = old.y;
//		item->pos.z_pos = old.z;
//	}
//
// End of new code.
//

	/* Double angle of turn if hit a wall (+ get correct room number) */
	if (shift_x || shift_z)
	{
		floor = GetFloor(item->pos.x_pos, y, item->pos.z_pos, &room_number);

		item->pos.y_rot += angle;
		if (tilt)
			CreatureTilt(item, (sint16)(tilt*2));
	}

	/* Check for biff against another baddie */
	if (item->object_number != TREX && item->speed && item->hit_points > 0)
		biff_angle = CreatureCreature(item_number);
	else
		biff_angle = 0;
	if (biff_angle)
	{
	//	item->pos.x_pos = old.x;
	//	item->pos.y_pos = old.y;
	//	item->pos.z_pos = old.z;
	//	item->pos.y_rot += (GetRandomControl() & 0x2000) - 0x1000; //TS - double angle of turn so baddies don't line up
		if (abs(biff_angle) < BIFF_AVOID_TURN)
			item->pos.y_rot -= biff_angle;
		else if (biff_angle > 0)
			item->pos.y_rot -= BIFF_AVOID_TURN;
		else
			item->pos.y_rot += BIFF_AVOID_TURN;
		return (1);
	}

	/* Do vertical dimension for creatures with a flying ability */
	if (LOT->fly != NO_FLYING)
	{
		dy = creature->target.y - item->pos.y_pos;
		if (dy > LOT->fly)
			dy = LOT->fly;
		else if (dy < -LOT->fly)
			dy = -LOT->fly;

		/* Vertical collision avoidance */
		height = GetHeight(floor, item->pos.x_pos, y, item->pos.z_pos);
		if (item->pos.y_pos + dy > height)
		{
			if (item->pos.y_pos > height)
			{
				item->pos.x_pos = old.x;
				item->pos.z_pos = old.z;
				dy = -LOT->fly;
			}
			else
			{
				dy = 0;
				item->pos.y_pos = height;
			}
		}
		else if (!objects[item->object_number].water_creature)
		{
			floor = GetFloor(item->pos.x_pos, y+STEP_L, item->pos.z_pos, &room_number);
			if (room[room_number].flags & (UNDERWATER | SWAMP))
			{
				dy = -LOT->fly;
			}
		}
		else
		{
			/* Avoid bashing your head */
			ceiling = GetCeiling(floor, item->pos.x_pos, y, item->pos.z_pos);

			/* Whale ignores bounds as it looks thin enough to get into gaps its bounds say it can't */
			if (item->object_number == WHALE)
				top = STEP_L/2;
			else
				top = bounds[2];

			if (item->pos.y_pos + top + dy < ceiling)
			{
				if (item->pos.y_pos + top < ceiling)
				{
					item->pos.x_pos = old.x;
					item->pos.z_pos = old.z;
					dy = LOT->fly;
				}
				else
					dy = 0;
			}
		}

		item->pos.y_pos += dy;
		floor = GetFloor(item->pos.x_pos, y, item->pos.z_pos, &room_number);
		item->floor = GetHeight(floor, item->pos.x_pos, y, item->pos.z_pos);

		/* Give the baddie some x rotation */
		angle = (item->speed)? phd_atan(item->speed, -dy) : 0;
		if (angle < -MAX_X_ROT)
			angle = -MAX_X_ROT;
		else if (angle > MAX_X_ROT)
			angle = MAX_X_ROT;

		if (angle < item->pos.x_rot - ONE_DEGREE)
			item->pos.x_rot -= ONE_DEGREE;
		else if (angle > item->pos.x_rot + ONE_DEGREE)
			item->pos.x_rot += ONE_DEGREE;
		else
			item->pos.x_rot = angle;
	}
	else
	{
		// Added by TS to stop baddies walking through crawl spaces
		floor = GetFloor(item->pos.x_pos, y, item->pos.z_pos, &room_number);
		ceiling = GetCeiling(floor, item->pos.x_pos, y, item->pos.z_pos);

		// Uncomment these lines if there are baddies whose height is not represented correctly by their bounds */
		if (item->object_number == TREX || item->object_number == SHIVA || item->object_number == MUTANT2)
			top = STEP_L*3;
		else
			top = bounds[2];

		if (item->pos.y_pos + top < ceiling)
		{
			item->pos.x_pos = old.x;
			item->pos.z_pos = old.z;
			item->pos.y_pos = old.y;
		}
		//End of new code

		floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
		item->floor = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);

		/* Slightly smoother baddie drop movement */
		if (item->pos.y_pos > item->floor)
			item->pos.y_pos = item->floor;
		else if (item->floor - item->pos.y_pos > STEP_L/4)
			item->pos.y_pos += STEP_L/4;
		else if (item->pos.y_pos < item->floor)
			item->pos.y_pos = item->floor;

		item->pos.x_rot = 0;
	}

	/* Get room slightly above feet in case in shallow water, else baddie drawn blue tinted */
	if (!objects[item->object_number].water_creature)
	{
		GetFloor(item->pos.x_pos, item->pos.y_pos-(STEP_L*2), item->pos.z_pos, &room_number);

		/* If THIS room is water filled, then baddie has just drowned! */
		if (room[room_number].flags & UNDERWATER)
			item->hit_points = 0;
	}

	if (item->room_number != room_number)
		ItemNewRoom(item_number, room_number);

	return (1);
}


sint16 CreatureTurn(ITEM_INFO *item, sint16 maximum_turn)
{
/* Calculate turning circle required to reach target and if this is less than twice
	the maximum turn rate then turn quickly */
	sint32 x, z, range;
	sint16 angle;
	CREATURE_INFO *creature;
//	sint32 feelxplus, feelzplus, feelxminus, feelzminus, feelxmid, feelzmid, feelplus, feelminus, feelmid, y;
//	sint16 room_number;
//	FLOOR_INFO *floor;

	if (item->data == NULL)
	{
		return (0);
	}

	/* If not moving, then don't turn */
	if (maximum_turn == 0)
		return (0);

	creature = (CREATURE_INFO *)item->data;

//
// New code by TS to get enemies to steer round steep triangles
//
/*
	y=item->pos.y_pos;
	room_number = item->room_number;
	floor = GetFloor(item->pos.x_pos , y, item->pos.z_pos , &room_number); // TS - should the feeler offsets be in here as well?
	GetHeight(floor, item->pos.x_pos+ (FEELER_DISTANCE * phd_sin(item->pos.y_rot) >> W2V_SHIFT), y, item->pos.z_pos+ (FEELER_DISTANCE * phd_cos(item->pos.y_rot) >> W2V_SHIFT));

	{
		feelxplus = item->pos.x_pos + (FEELER_DISTANCE * phd_sin(item->pos.y_rot + FEELER_ANGLE) >> W2V_SHIFT);
		feelzplus = item->pos.z_pos + (FEELER_DISTANCE * phd_cos(item->pos.y_rot + FEELER_ANGLE) >> W2V_SHIFT);
		floor = GetFloor(feelxplus, y, feelzplus, &room_number);
		GetHeight(floor, feelxplus, y, feelzplus);
		feelplus = height_type;
		feelxminus = item->pos.x_pos + (FEELER_DISTANCE * phd_sin(item->pos.y_rot - FEELER_ANGLE) >> W2V_SHIFT);
		feelzminus = item->pos.z_pos + (FEELER_DISTANCE * phd_cos(item->pos.y_rot - FEELER_ANGLE) >> W2V_SHIFT);
		floor = GetFloor(feelxminus, y, feelzminus, &room_number);
		GetHeight(floor, feelxminus, y, feelzminus);
		feelminus = height_type;
		feelxmid = item->pos.x_pos + (FEELER_DISTANCE * phd_sin(item->pos.y_rot) >> W2V_SHIFT);
		feelzmid = item->pos.z_pos + (FEELER_DISTANCE * phd_cos(item->pos.y_rot) >> W2V_SHIFT);
		floor = GetFloor(feelxmid, y, feelzmid, &room_number);
		GetHeight(floor, feelxmid, y, feelzmid);
		feelmid = height_type;
//		printf("\nfeelplus = %d, feelmid = %d, feelminus = %d", feelplus, feelmid, feelminus);

#ifdef DEBUG_BOX

//	sprintf(exit_message, "feelplus: %d, feelmid: %d, feelminus: %d", feelplus, feelmid, feelminus);
//	PrintDbug(2, 3, exit_message);

#endif
// - This worked for slopes as well as triangles - slopes now dealt with in BadFloor

		if (feelminus>1 && feelminus<4 && feelmid>1 && feelmid<4)
		{
			creature->target.x = feelxplus;
			creature->target.z = feelzplus;
		}
		else if (feelplus>1 && feelplus<4 && feelmid>1 && feelmid<4)
		{
			creature->target.x = feelxminus;
			creature->target.z = feelzminus;
		}
		else if (feelplus==3 || feelplus==2 || feelminus==3 || feelminus==2)
		{
			creature->target.x = feelxmid;
			creature->target.z = feelzmid;
		}


		if (feelminus==3 && feelmid==3)
		{
			creature->target.x = feelxplus;
			creature->target.z = feelzplus;
		}
		else if (feelplus==3 && feelmid==3)
		{
			creature->target.x = feelxminus;
			creature->target.z = feelzminus;
		}
		else if (feelplus==3 || feelminus==3)
		{
			creature->target.x = feelxmid;
			creature->target.z = feelzmid;
		}

	}
*/
//
// End of new code.
//

	z = creature->target.z - item->pos.z_pos;
	x = creature->target.x - item->pos.x_pos;

	angle = phd_atan(z, x) - item->pos.y_rot;

	/* NOTE: The following slow turn method works perfectly in clear terrain by avoiding the situation
			of Lara standing in the middle of the creatures turning circle. However, in complex terrain
			it is still possible to end up in cyclic patterns that don't quite target Lara. Implies all
			baddies should break off from ATTACK mood, or change their stride occassionally to avoid
			such effects. */

			/* NOTE: The range used here depends the max turn circle of the creature;
			use 1.5-2 times the turn circle radius for best results it seems. Correct calc of
			radius is:
			item->speed * phd_cos(maximum_turn/2) / (phd_sin(maximum_turn/2) * 2);
			but as 'maximum_turn/2' is small and this is only approximate anyway, use:
			item->speed / RADIANS(maximum_turn);
	which, for 1.6 times, gives: */
	range = (item->speed << 14) / maximum_turn;

	/* Use slow turn if a) not facing target b) within a certain range */
	if ((angle > FRONT_ARC || angle < -FRONT_ARC) && x*x+z*z < SQUARE(range))
		maximum_turn >>= 1;

	if (angle > maximum_turn)
		angle = maximum_turn;
	else if (angle < -maximum_turn)
		angle = -maximum_turn;

	item->pos.y_rot += angle;

	/*
	//Start of CreatureCreature frig - TS - 27/7/98
	old.x = item->pos.x_pos;
	old.z = item->pos.z_pos;
	item->pos.x_pos += (item->speed * phd_sin(item->pos.y_rot) >> W2V_SHIFT);
	item->pos.z_pos += (item->speed * phd_cos(item->pos.y_rot) >> W2V_SHIFT);
	biff_angle = CreatureCreature(creature->item_num);
	if (biff_angle)
	{
		if (biff_angle > 0)
		{
			item->pos.y_rot -= BIFF_AVOID_TURN;
			angle -= BIFF_AVOID_TURN;
		}
		else
		{
			item->pos.y_rot += BIFF_AVOID_TURN;
			angle += BIFF_AVOID_TURN;
		}

	}
	item->pos.x_pos = old.x;
	item->pos.z_pos = old.z;
	//End of CreatureCreature frig
	*/

	return (angle);
}

void CreatureTilt(ITEM_INFO *item, sint16 angle)
{
	angle = (angle << 2) - item->pos.z_rot;
	if (angle < -MAX_TILT)
		angle = -MAX_TILT;
	else if (angle > MAX_TILT)
		angle = MAX_TILT;

	item->pos.z_rot += angle;
}

/*
void CreatureHead(ITEM_INFO *item, sint16 required)
{
	sint16 change;
	CREATURE_INFO *creature;

	if (item->data == NULL)
		return;

	creature = (CREATURE_INFO *)item->data;

	// Limit rate of change
	change = required - creature->head_rotation;
	if (change > MAX_HEAD_CHANGE)
		change = MAX_HEAD_CHANGE;
	else if (change < -MAX_HEAD_CHANGE)
		change = -MAX_HEAD_CHANGE;

	creature->head_rotation += change;

	// Limit angle
	if (creature->head_rotation > HEAD_ARC)
		creature->head_rotation = HEAD_ARC;
	else if (creature->head_rotation < -HEAD_ARC)
		creature->head_rotation = -HEAD_ARC;
}


void CreatureNeck(ITEM_INFO *item, sint16 required)
{
// Allows adjustment of extra_rotation 2: may want to increase 'creature_info' to have more than 2 rotations
//	(say 4 instead) and then have a more general routine to adjust them
	sint16 change;
	CREATURE_INFO *creature;

	if (item->data == NULL)
		return;

	creature = (CREATURE_INFO *)item->data;

	// Limit rate of change
	change = required - creature->neck_rotation;
	if (change > MAX_HEAD_CHANGE)
		change = MAX_HEAD_CHANGE;
	else if (change < -MAX_HEAD_CHANGE)
		change = -MAX_HEAD_CHANGE;

	creature->neck_rotation += change;

	// Limit angle
	if (creature->neck_rotation > HEAD_ARC)
		creature->neck_rotation = HEAD_ARC;
	else if (creature->neck_rotation < -HEAD_ARC)
		creature->neck_rotation = -HEAD_ARC;
}
*/

void CreatureJoint(ITEM_INFO *item, sint16 joint, sint16 required)
{
	// This routine replaces CreatureHead and CreatureNeck... creatures now have up to 4 joints that can be manipulated

	sint16 change;
	CREATURE_INFO *creature;

	if (item->data == NULL)
		return;

	creature = (CREATURE_INFO *)item->data;

	/* Limit rate of change */
	change = required - creature->joint_rotation[joint];
	if (change > MAX_JOINT_CHANGE)
		change = MAX_JOINT_CHANGE;
	else if (change < -MAX_JOINT_CHANGE)
		change = -MAX_JOINT_CHANGE;

	creature->joint_rotation[joint] += change;

	/* Limit angle */
	if (creature->joint_rotation[joint] > JOINT_ARC)
		creature->joint_rotation[joint] = JOINT_ARC;
	else if (creature->joint_rotation[joint] < -JOINT_ARC)
		creature->joint_rotation[joint] = -JOINT_ARC;
}

void CreatureFloat(sint16 item_number)
{
	/* Make dead underwater creatures float to the surface */
	sint32 water_level;
	ITEM_INFO *item;
	FLOOR_INFO *floor;
	sint16 room_number;

	item = &items[item_number];

	item->hit_points = DONT_TARGET;
	item->pos.x_rot = 0;

	water_level = GetWaterHeight(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, item->room_number);
	// if (water_level == NO_HEIGHT) => what happens (water has been drained away in flip map)
	if (item->pos.y_pos > water_level)
		item->pos.y_pos -= CREATURE_FLOAT_SPEED;
	if (item->pos.y_pos < water_level)
		item->pos.y_pos = water_level;

	AnimateItem(item);

	room_number = item->room_number;
	floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
	item->floor = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	if (room_number != item->room_number)
		ItemNewRoom(item_number, room_number);

	/* Deactivate when reach surface */
	/*
	NOTE : not in operation at present as it causes some problems (baddie can deactivate prematurely
	if its death throes move the origin above the water surface), and means that baddies won't respond
	to changes in water level

	  if (item->pos.y_pos <= water_level)
	  {
	  item->pos.y_pos = water_level;
	  item->status = DEACTIVATED;
	  item->collidable = 0;
	  DisableBaddieAI(item_number);
	  RemoveActiveItem(item_number);
	  }
	*/
}


void CreatureUnderwater(ITEM_INFO *item, sint32 depth)
{
/* Keep creature underwater at minimum 'depth' and remove x_rot whilst they swim at that depth */
	sint32 water_level;

	water_level = GetWaterHeight(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, item->room_number);
	if (item->pos.y_pos < water_level + depth)
	{
		item->pos.y_pos = water_level + depth;
		if (item->pos.x_rot > 2*ONE_DEGREE)
			item->pos.x_rot -= 2*ONE_DEGREE;
		else if (item->pos.x_rot > 0)
			item->pos.x_rot = 0;
	}
}


sint16 CreatureEffect(ITEM_INFO *item, BITE_INFO *bite,
							 sint16 (*generate)(sint32 x, sint32 y, sint32 z, sint16 speed, sint16 yrot, sint16 room_number))
{
	PHD_VECTOR	pos;

	pos.x = bite->x;             // Copy Offsets from mesh
	pos.y = bite->y;             // Pivot
	pos.z = bite->z;
	GetJointAbsPosition(item, &pos, bite->mesh_num );
	return ((*generate)(pos.x, pos.y, pos.z, item->speed, item->pos.y_rot, item->room_number));
}


int CreatureVault(sint16 item_number, sint16 angle, int vault, int shift)
{
	/* Allows creatures to vault up onto high steps.
		Need to pass the max height allowable, where 2=half block, 3=3/4 block, 4=full block,
	 	and it will return the height that is vaulted (or 0) */
	ITEM_INFO *item;
	sint32 y,  xx, yy, x_floor, y_floor;
	sint16 room_number;

	item = &items[item_number];

	/* Remember current position of creature in case it needs to vault up */
	xx = item->pos.z_pos >> WALL_SHIFT;
	yy = item->pos.x_pos >> WALL_SHIFT;
	y = item->pos.y_pos;
	room_number = item->room_number;

	CreatureAnimation(item_number, angle, 0);

	/* Check for double block vault situation (1.5 blocks or more height change) */
	if (item->floor > y + STEP_L*7/2)
		vault = -4;
	else if (item->floor > y + STEP_L*5/2 && item->object_number == MONKEY)		//Only monkeys have anims for going down 2 or 3 blocks
		vault = -3;
	else if (item->floor > y + STEP_L*3/2 && item->object_number == MONKEY)
		vault = -2;
	else if (item->pos.y_pos > y - STEP_L*3/2)
		return 0;
	else if (item->pos.y_pos > y - STEP_L*5/2)
		vault = 2;
	else if (item->pos.y_pos > y - STEP_L*7/2)
		vault = 3;
	else
		vault = 4;

	/* Jump creature to correct position */
	x_floor = item->pos.z_pos >> WALL_SHIFT;
	y_floor = item->pos.x_pos >> WALL_SHIFT;
	if (xx == x_floor)
	{
		if (yy == y_floor)
			return 0;

		if (yy < y_floor)
		{
			item->pos.x_pos = (y_floor << WALL_SHIFT) - shift;
			item->pos.y_rot = 0x4000;
		}
		else
		{
			item->pos.x_pos = (yy << WALL_SHIFT) + shift;
			item->pos.y_rot = -0x4000;
		}
	}
	else if (yy == y_floor)
	{
		if (xx < x_floor)
		{
			item->pos.z_pos = (x_floor << WALL_SHIFT) - shift;
			item->pos.y_rot = 0;
		}
		else
		{
			item->pos.z_pos = (xx << WALL_SHIFT) + shift;
			item->pos.y_rot = -0x8000;
		}
	}
	// else a diagonal collision - never seems to happen...

	item->pos.y_pos = item->floor = y;
	if (room_number != item->room_number)
		ItemNewRoom(item_number, room_number);

	return vault;
}


void CreatureKill(ITEM_INFO *item, int kill_anim, int kill_state, int lara_kill_state)
{
	item->anim_number = objects[item->object_number].anim_index + kill_anim;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = kill_state;

	/* Jump to animation state in LARA_EXTRA */
	lara_item->anim_number = objects[LARA_EXTRA].anim_index;
	lara_item->frame_number = anims[lara_item->anim_number].frame_base;
	lara_item->current_anim_state = EXTRA_BREATH;
	lara_item->goal_anim_state = lara_kill_state;

	/* Move to position */
	lara_item->pos.x_pos = item->pos.x_pos;
	lara_item->pos.y_pos = item->pos.y_pos;
	lara_item->pos.z_pos = item->pos.z_pos;
	lara_item->pos.y_rot = item->pos.y_rot;
	lara_item->pos.x_rot = item->pos.x_rot;
	lara_item->pos.z_rot = item->pos.z_rot;
	lara_item->fallspeed = 0;
	lara_item->gravity_status = 0;
	lara_item->speed = 0;

	if (item->room_number != lara_item->room_number)
		ItemNewRoom(lara.item_number, item->room_number);

	/* Jump to that animation */
	AnimateItem(lara_item);
//	lara_item->current_anim_state = lara_item->goal_anim_state = lara_kill_state; // just to make sure?

	/* Flag that Lara is running animations from another project */
	lara.extra_anim = 1;
	lara.gun_status = LG_HANDSBUSY;
	lara.gun_type = LG_UNARMED;
	lara.hit_direction = -1;
	lara.air = -1;

	camera.pos.room_number = lara_item->room_number; // this may not be correct, but close enough

// TS - added these lines to test stopping camera going behind creature
// Only works when Lara's unarmed though
	camera.type = CHASE_CAMERA;
	camera.flags = FOLLOW_CENTRE;
	camera.target_angle = 170*ONE_DEGREE;
	camera.target_elevation = -25*ONE_DEGREE;

}

/*
#define TARGET_TOLERANCE SQUARE(WALL_L*2)
#define BADDY 0
#define GOODY 1
#define RAPTOR_OBJ 2
#define RAPTOR_INFIGHTING_CHANCE 0x1000

void GetBaddieTarget(sint16 item_number, int goody)
{
	int slot, distance, best_distance, x, y, z;
	CREATURE_INFO *cinfo, *creature;
	ITEM_INFO *item, *target, *best, *target_item;
	sint16 i;

	item = &items[item_number];
	creature = (CREATURE_INFO *)item->data;

	best = NULL;
	best_distance = 0x7fffffff;

	if (item->object_number == TREX)
	{
		long	lp,target_number;

		GetNearByRooms(item->pos.x_pos,item->pos.y_pos,item->pos.z_pos,4096,0,item->room_number);

		for (lp=0;lp<number_draw_rooms;lp++)
		{
			for (target_number = room[draw_rooms[lp]].item_number; target_number!=NO_ITEM; target_number=target->next_item)
			{
				target = &items[target_number];
				if (target->object_number != FLARE_ITEM || !target->hit_points)
					continue;

				// Is this target closest?
				x = (target->pos.x_pos - item->pos.x_pos) >> 6;
				y = (target->pos.y_pos - item->pos.y_pos) >> 6;
				z = (target->pos.z_pos - item->pos.z_pos) >> 6;
				distance = x*x + y*y + z*z;
				if (distance < best_distance)
				{
					best = target;
					best_distance = distance;
				}
			}
		}
	}

	if (item->object_number != TREX || best == NULL)
	{
		cinfo = baddie_slots;
		for (slot=0; slot<NUM_SLOTS; slot++, cinfo++)
		{
			if (cinfo->item_num==NO_ITEM || cinfo->item_num==item_number)
				continue;

			// Is this a possible enemy?
			target = &items[cinfo->item_num];
			switch (goody)
			{
				case GOODY:
					break;
				case BADDY:
					break;
				case RAPTOR_OBJ:	// Can't use this 'cos of objects.h!
					if (target->object_number != RAPTOR || (GetRandomControl() > RAPTOR_INFIGHTING_CHANCE)) //Remember this dog's a RAPTOR - TS
						continue;
					break;
				default:
					continue;
			}

			// Is this target closest?
			x = (target->pos.x_pos - item->pos.x_pos) >> 6;
			y = (target->pos.y_pos - item->pos.y_pos) >> 6;
			z = (target->pos.z_pos - item->pos.z_pos) >> 6;
			distance = x*x + y*y + z*z;
			if (distance < best_distance)
			{
				best = target;
				best_distance = distance;
			}
		}
	}

	if (goody == BADDY)
	{
		// TS - testing for creatures going toward a target object
	//	for (i=level_items, target_item=&items[level_items]; i<NUMBER_ITEMS; i++, target_item++)
		for (i=0, target_item=&items[0]; i<level_items; i++, target_item++)

	//	if (item->active && item->object_number == FLARE_ITEM)
	//		number_flares++;
		{
	//			printf("\nChecking object...%d",target_item->object_number);
			if (target_item->active && target_item->object_number == ANIMATING1)
			{
				creature->enemy = target_item;
//				PrintDbug(2, 3, "Targetting the flag");
				return;
			}
		}

	}

	// No alternative target
	if (best == NULL)
	{
		if (goody==GOODY && !monks_attack_lara)
			creature->enemy = NULL;
		else
			creature->enemy = lara_item;
		return;
	}

	// Lara may be better than this
	if ((goody!=GOODY || monks_attack_lara) && item->object_number != TREX)
	{
		x = (lara_item->pos.x_pos - item->pos.x_pos) >> 6;
		y = (lara_item->pos.y_pos - item->pos.y_pos) >> 6;
		z = (lara_item->pos.z_pos - item->pos.z_pos) >> 6;
		distance = x*x + y*y + z*z;
		if (distance < best_distance)
		{
			best = lara_item;
			best_distance = distance;
		}
	}

	// If we don't have a target yet, this is the one
	if (!creature->enemy || creature->enemy->status!=ACTIVE)
	{
		creature->enemy = best;
		return;
	}

	// Possible alternative target?

	if (item->object_number != TREX)
	{
		target = creature->enemy;
		x = (target->pos.x_pos - item->pos.x_pos) >> 6;
		y = (target->pos.y_pos - item->pos.y_pos) >> 6;
		z = (target->pos.z_pos - item->pos.z_pos) >> 6;
		distance = x*x + y*y + z*z;
		if (distance < best_distance + TARGET_TOLERANCE)
			creature->enemy = best;
	}

	// no change
}

*/

void AlertAllGuards(sint16 item_number)
{
	int slot;
	CREATURE_INFO *cinfo;
	ITEM_INFO *target, *item;
	sint16 obj_number;

	cinfo = baddie_slots;
	item = &items[item_number];
	obj_number = item->object_number;

	for (slot=0; slot<NUM_SLOTS; slot++, cinfo++)
	{
		if (cinfo->item_num==NO_ITEM)
			continue;

		target = &items[cinfo->item_num];

		if (obj_number == target->object_number && target->status == ACTIVE)
			cinfo->alerted = 1;
	}
}


void AlertNearbyGuards(ITEM_INFO *item)
{
	int slot;
	CREATURE_INFO *cinfo;
	ITEM_INFO *target;
	sint32 x, y, z, distance;

	cinfo = baddie_slots;

	for (slot=0; slot<NUM_SLOTS; slot++, cinfo++)
	{
		if (cinfo->item_num==NO_ITEM)
			continue;

		target = &items[cinfo->item_num];

		if (target->room_number == item->room_number)
		{
			cinfo->alerted = 1;
//			target->hit_status = 1;  //Not sure about this one
			continue;
		}

		x = (target->pos.x_pos - item->pos.x_pos) >> 6;
		y = (target->pos.y_pos - item->pos.y_pos) >> 6;
		z = (target->pos.z_pos - item->pos.z_pos) >> 6;
		distance = x*x + y*y + z*z;

		if (distance < GUNFIRE_ALERT_RADIUS)
		{
			cinfo->alerted = 1;
//			target->hit_status = 1;  //Not sure about this one
		}
	}
}

sint16 AIGuard(CREATURE_INFO *creature)
{
	int random;

	if (items[creature->item_num].ai_bits & MODIFY) //MODIFY modifies Guard behaviour so he just looks straight ahead
		return(0);

	random = GetRandomControl();
	if (random < 0x100) //Head Centre
	{
		creature->head_left = 1;
		creature->head_right = 1;
	}
	else if (random < 0x180) //Head Left
	{
		creature->head_left = 1;
		creature->head_right = 0;
	}
	else if (random < 0x200) //Head Right
	{
		creature->head_left = 0;
		creature->head_right = 1;
	}

	if (creature->head_left && creature->head_right)
		return(0);
	else if (creature->head_left)
		return(-0x4000);
	else if (creature->head_right)
		return(0x4000);
	return(0);
}


void		GetAITarget (CREATURE_INFO *creature)
{
	ITEM_INFO *item, *target_item, *enemy;
	FLOOR_INFO *floor;
	char ai_bits;
	int i;
	sint16 enemy_object;


	enemy = creature->enemy;

//	if(enemy == NULL) return;

	if (enemy == NULL)
		enemy_object = NULL_OBJECT;
	else
		enemy_object = enemy->object_number;

	item = 	&items[creature->item_num];

	ai_bits = item->ai_bits;

	if (ai_bits & GUARD)
	{
		creature->enemy = lara_item;
		if (creature->alerted)
		{
			item->ai_bits &= ~GUARD;	//Remove guard job once alerted
			if (ai_bits & AMBUSH)
				item->ai_bits |= MODIFY;	//So if he has got other behaviours, he carries them out straight away
		}
	}
	else if (ai_bits & PATROL1)
	{
		if (creature->alerted || creature->hurt_by_lara)  //creature should be alerted once it is hurt anyway, so hurt_by_lara clause is probably redundant, but just to be safe...
		{
			item->ai_bits &= ~PATROL1; //Remove patrol job once alerted
			if (ai_bits & AMBUSH)
				item->ai_bits |= MODIFY;	//So if he has got other behaviours, he carries them out straight away

		}
		else if (!creature->patrol2 && enemy_object != AI_PATROL1)	//Need to find Patrol1 point
		{
			for (i=0, target_item=&items[0]; i<level_items; i++, target_item++)
			{
				if (target_item->object_number == AI_PATROL1 && target_item->room_number != NO_ROOM)	//Find the patrol1 point
				{
					if (SameZone(creature, target_item) && target_item->pos.y_rot == item->item_flags[3])	//zone check to make sure the baddy can get to it & orientation check to match AI points to pickup
					{
						creature->enemy = target_item;		//this allows multiple ambush points per level
						break;
					}
				}
			}

		}
		else if (creature->patrol2 && enemy_object != AI_PATROL2)	//Need to find Patrol2 point
		{
			for (i=0, target_item=&items[0]; i<level_items; i++, target_item++)
			{
				if (target_item->object_number == AI_PATROL2 && target_item->room_number != NO_ROOM)	//Find the patrol2 point
				{
					if (SameZone(creature, target_item) && target_item->pos.y_rot == item->item_flags[3])	//zone check to make sure the baddy can get to it & orientation check to match AI points to pickup
					{
						creature->enemy = target_item;		//this allows multiple ambush points per level
						break;
					}
				}
			}
		}
		else if (ABS(enemy->pos.x_pos - item->pos.x_pos) < REACHED_GOAL_RADIUS &&	//Reached the patrol point, so swap to the other one
					ABS(enemy->pos.z_pos - item->pos.z_pos) < REACHED_GOAL_RADIUS &&
					ABS(enemy->pos.y_pos - item->pos.y_pos) < REACHED_GOAL_RADIUS)
		{
			floor = GetFloor(enemy->pos.x_pos, enemy->pos.y_pos, enemy->pos.z_pos, &(enemy->room_number));
			GetHeight(floor, enemy->pos.x_pos, enemy->pos.y_pos, enemy->pos.z_pos);
			TestTriggers(trigger_index, 1);

			creature->patrol2 = ~creature->patrol2;
		}
	}
	else if (ai_bits & AMBUSH)
	{
		if (!(ai_bits & MODIFY) && !creature->hurt_by_lara)	//if MODIFY is picked up, creature will head straight to the ambush point as soon as it is activated
			creature->enemy = lara_item;						//otherwise it will only run for the ambush point once it has been hurt
		else if (enemy_object != AI_AMBUSH)
		{
			for (i=0, target_item=&items[0]; i<level_items; i++, target_item++)
			{
				if (target_item->object_number == AI_AMBUSH && target_item->room_number != NO_ROOM)	//Find the ambush point
				{
					if (SameZone(creature, target_item) && (target_item->pos.y_rot == item->item_flags[3]  || item->object_number == MONKEY))	//zone check to make sure the baddy can get to it
					{
						creature->enemy = target_item;		//this allows multiple ambush points per level
						break;
					}
				}
			}
		}
		else if (item->object_number == MONKEY)
			return;
		else if (ABS(enemy->pos.x_pos - item->pos.x_pos) < REACHED_GOAL_RADIUS &&
					ABS(enemy->pos.z_pos - item->pos.z_pos) < REACHED_GOAL_RADIUS &&
					ABS(enemy->pos.y_pos - item->pos.y_pos) < REACHED_GOAL_RADIUS)
		{
			//Testing trigger code
			floor = GetFloor(enemy->pos.x_pos, enemy->pos.y_pos, enemy->pos.z_pos, &(enemy->room_number));
			GetHeight(floor, enemy->pos.x_pos, enemy->pos.y_pos, enemy->pos.z_pos);
			TestTriggers(trigger_index, 1);

			creature->reached_goal = 1;	//Turn and fight!
			creature->enemy = lara_item;
			item->ai_bits &= ~AMBUSH;	//Remove ambush job once the ambush point is reached...
			item->ai_bits &= ~MODIFY;	//...remove modify in case it was used as a modifier...
			item->ai_bits |= GUARD;		//... and give it a guard job...
			creature->alerted = 0;		//...and turn off alerted so it stays there


			//KillItem?
		}
	}
	else if (ai_bits & FOLLOW)
	{
		if (creature->hurt_by_lara)		//If Lara shoots me, the deal's off
		{
			creature->enemy = lara_item;
			creature->alerted = 1; //just to be safe
			item->ai_bits &= ~FOLLOW;
		}
		else if (item->hit_status)
		{
			item->ai_bits &= ~FOLLOW;
		}
		else if (enemy_object != AI_FOLLOW)
		{
			for (i=0, target_item=&items[0]; i<level_items; i++, target_item++)
			{
				if (target_item->object_number == AI_FOLLOW && target_item->room_number != NO_ROOM)	//Find the follow point
				{
					if (SameZone(creature, target_item) && target_item->pos.y_rot == item->item_flags[3])	//zone check to make sure the baddy can get to it
					{
						creature->enemy = target_item;		//this allows multiple follow points per level
						break;
					}
				}
			}
		}
		else if (ABS(enemy->pos.x_pos - item->pos.x_pos) < REACHED_GOAL_RADIUS &&
					ABS(enemy->pos.z_pos - item->pos.z_pos) < REACHED_GOAL_RADIUS &&
					ABS(enemy->pos.y_pos - item->pos.y_pos) < REACHED_GOAL_RADIUS)
		{
			creature->reached_goal = 1;	//Just stand and wait
			item->ai_bits &= ~FOLLOW;
		}

	}
	else if (item->object_number == MONKEY && item->carried_item == NO_ITEM)
	{
		if (item->ai_bits != MODIFY)
		{
			 if (enemy_object != MEDI_ITEM)
			{
				for (i=0, target_item=&items[0]; i<level_items; i++, target_item++)
				{
					if (target_item->object_number == MEDI_ITEM && target_item->room_number != NO_ROOM && !target_item->ai_bits && target_item->status != INVISIBLE && !(target_item->flags & KILLED_ITEM))	//Find the follow point
					{
						if (SameZone(creature, target_item))	//zone check to make sure the baddy can get to it
						{
							creature->enemy = target_item;		//this allows multiple follow points per level
							break;
						}
					}
				}
			}
		}
		else 
		{
			if (enemy_object != KEY_ITEM4)
			{
				for (i=0, target_item=&items[0]; i<level_items; i++, target_item++)
				{
					if (target_item->object_number == KEY_ITEM4 && target_item->room_number != NO_ROOM && !target_item->ai_bits && target_item->status != INVISIBLE && !(target_item->flags & KILLED_ITEM))	//Find the follow point
					{
						if (SameZone(creature, target_item))	//zone check to make sure the baddy can get to it
						{
							creature->enemy = target_item;		//this allows multiple follow points per level
							break;
						}
					}
				}
			}
		}

		 /*
		else if (ABS(enemy->pos.x_pos - item->pos.x_pos) < REACHED_GOAL_RADIUS &&
					ABS(enemy->pos.z_pos - item->pos.z_pos) < REACHED_GOAL_RADIUS)
		{
//printf("enemy: %p, items: %p, sizeof(ITEM_INFO): %d, item no: %d \n", enemy, items,sizeof(ITEM_INFO),(enemy - items) / sizeof(ITEM_INFO));
			creature->reached_goal = 1;	//Just stand and wait
//			i= (enemy - items) / sizeof(ITEM_INFO); //work out the item number
			i= enemy - items;
			item->carried_item = i;
			RemoveDrawnItem(i);
			enemy->room_number = NO_ROOM;
			creature->enemy = NULL;
		}
		*/
	}

}

sint16		SameZone (CREATURE_INFO *creature, ITEM_INFO *target_item)
{
	ROOM_INFO *r;
	sint16 *zone, zone_number, target_zone;
	ITEM_INFO *item;


	if (creature->LOT.fly == NO_FLYING)
		zone = ground_zone[ZONE(creature->LOT.step)][flip_status];
	else
		return(1);

	item = 	&items[creature->item_num];


	/* Basic information about creature */
	r = &room[item->room_number];
	item->box_number = r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) +
		((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].box;
	zone_number = zone[item->box_number];
	/* Basic information about target */
	r = &room[target_item->room_number];
	target_item->box_number = r->floor[((target_item->pos.z_pos - r->z) >> WALL_SHIFT) +
		((target_item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].box;
	target_zone = zone[target_item->box_number];

#ifdef DEBUG_BOX

//	sprintf(exit_message, "Target Box:%d, My Box:%d, Lara's Box:%d", LOT->target_box, item->box_number, lara_item->box_number);
//	PrintDbug(2, 1, exit_message);
//	sprintf(exit_message, "Zone:%d, Target Zone:%d", zone_number, target_zone);
//	PrintDbug(2,5, exit_message);

#endif

	if (zone_number == target_zone)
		return(1);
	else
		return(0);
}


void AdjustStopperFlag(ITEM_INFO *item, long dir, long set)
{
	ROOM_INFO *r;
	sint16 room_number;
	long	x,z;

	x = item->pos.x_pos;
	z = item->pos.z_pos;
	r = &room[item->room_number];
	r->floor[((z - r->z) >> WALL_SHIFT) + ((x - r->x) >> WALL_SHIFT) * r->x_size].stopper = set;

	x = item->pos.x_pos + ((1024 * phd_sin(dir)) >> W2V_SHIFT);
	z = item->pos.z_pos + ((1024 * phd_cos(dir)) >> W2V_SHIFT);
	room_number = item->room_number;
	GetFloor(x,item->pos.y_pos,z,&room_number);
	r = &room[room_number];
	r->floor[((z - r->z) >> WALL_SHIFT) + ((x - r->x) >> WALL_SHIFT) * r->x_size].stopper = set;
}
