/*********************************************************************************************/
/*                                                                                           */
/* People Control                                                                            */
/*                                                                                           */
/*********************************************************************************************/

// CONTAINS TOMB2 GOONS

#include "game.h"

#define CULT1_SHOT_DAMAGE 50
#define CULT3_SHOT_DAMAGE 50
#define WORK1_SHOT_DAMAGE 150
#define WORK2_SHOT_DAMAGE 30
#define BANDIT_SHOT_DAMAGE 8
#define BANDIT2_SHOT_DAMAGE 50

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

// CULT 1: venice goon
#define CULT1_POSE_CHANCE (0x500)
#define CULT1_UNPOSE_CHANCE (0x100)
#define CULT1_WALK_CHANCE (CULT1_POSE_CHANCE + 0x500)
#define CULT1_UNWALK_CHANCE (0x300)

#define CULT1_RUN_RANGE SQUARE(WALL_L*2)

BITE_INFO cult1_gun = {3,331,56, 10};

enum cult1_anims {CULT1_EMPTY, CULT1_WALK, CULT1_RUN, CULT1_STOP, CULT1_WAIT1, CULT1_WAIT2,
	CULT1_AIM1, CULT1_SHOOT1, CULT1_AIM2, CULT1_SHOOT2, CULT1_AIM3, CULT1_SHOOT3, CULT1_DEATH};

#define CULT1_WALK_TURN (ONE_DEGREE*5)
#define CULT1_RUN_TURN (ONE_DEGREE*5)

#define CULT1_DIE_ANIM 20 // 2 deaths


// CULT 3: the Cult Lord
enum cult3_anims {CULT3_EMPTY, CULT3_STOP, CULT3_WAIT, CULT3_WALK, CULT3_RUN, CULT3_AIML, CULT3_AIMR, CULT3_SHOOTL, CULT3_SHOOTR, CULT3_AIM2, CULT3_SHOOT2, CULT3_DEATH};

BITE_INFO cult3_gunl = {-2,275,23, 6};
BITE_INFO cult3_gunr = {2,275,23, 10};

#define CULT3_RUN_TURN (ONE_DEGREE*3)
#define CULT3_WALK_TURN (ONE_DEGREE*3)

#define CULT3_STOP_RANGE SQUARE(WALL_L*3)
#define CULT3_RUN_RANGE  SQUARE(WALL_L*5)

#define CULT3_WAIT_ANIM 3
#define CULT3_DIE_ANIM 32


// WORKER 1: shotgun
BITE_INFO work1_gun = {0,281,40, 9};

enum work1_anims {WORK1_EMPTY, WORK1_WALK, WORK1_STOP, WORK1_WAIT, WORK1_SHOOT1, WORK1_RUN, WORK1_SHOOT2, WORK1_DEATH, WORK1_AIM, WORK1_AIM2, WORK1_SHOOT3};

#define WORK1_WALK_TURN (ONE_DEGREE*3)
#define WORK1_RUN_TURN (ONE_DEGREE*5)

#define WORK1_RUN_RANGE SQUARE(WALL_L*2)
#define WORK1_SHOOT1_RANGE SQUARE(WALL_L*3)

#define WORK1_DIE_ANIM 18


// WORKER2: machine gun man
// WORKER4: flame thrower man
BITE_INFO work2_gun = {0,308,32, 9};

enum work2_anims {WORK2_EMPTY, WORK2_STOP, WORK2_WALK, WORK2_RUN, WORK2_WAIT, WORK2_SHOOT1, WORK2_SHOOT2, WORK2_DEATH, WORK2_AIM1, WORK2_AIM2, WORK2_AIM3, WORK2_SHOOT3};

#define WORK2_WALK_TURN (ONE_DEGREE*3)
#define WORK2_RUN_TURN (ONE_DEGREE*5)

#define WORK2_RUN_RANGE SQUARE(WALL_L*2)
#define WORK2_SHOOT1_RANGE SQUARE(WALL_L*3)

#define WORK2_DIE_ANIM 19


// BANDIT1: Tibet goon with Uzi
BITE_INFO bandit_gun = {-2,150,19, 17};

enum bandit_anims {BANDIT_EMPTY, BANDIT_WAIT, BANDIT_WALK, BANDIT_RUN, BANDIT_AIM1, BANDIT_SHOOT1, BANDIT_AIM2,
	BANDIT_SHOOT2, BANDIT_SHOOT3A, BANDIT_SHOOT3B, BANDIT_SHOOT4A, BANDIT_AIM3, BANDIT_AIM4, BANDIT_DEATH, BANDIT_SHOOT4B};

#define BANDIT_WALK_TURN (ONE_DEGREE*4)
#define BANDIT_RUN_TURN (ONE_DEGREE*6)

#define BANDIT_WALK_RANGE SQUARE(WALL_L*2)

#define BANDIT_DIE_ANIM 14


// BANDIT2: Tibet goon with laser
BITE_INFO bandit2_gun = {-1,230,9, 17};

enum bandit2_anims {BANDIT2_EMPTY, BANDIT2_AIM4, BANDIT2_WAIT, BANDIT2_WALK, BANDIT2_RUN, BANDIT2_AIM1, BANDIT2_AIM2,
	BANDIT2_SHOOT1, BANDIT2_SHOOT2, BANDIT2_SHOOT4A, BANDIT2_SHOOT4B, BANDIT2_DEATH, BANDIT2_AIM5, BANDIT2_SHOOT5};

#define BANDIT2_WALK_TURN (ONE_DEGREE*4)
#define BANDIT2_RUN_TURN (ONE_DEGREE*6)

#define BANDIT2_WALK_RANGE SQUARE(WALL_L*2)

#define BANDIT2_DIE_ANIM 9


/* How close before a baddie will open fire */
#define PEOPLE_SHOOT_RANGE SQUARE(WALL_L*8)
#define PEOPLE_HIT_CHANCE 0x2000
#define PEOPLE_TARGETING_SPEED 300

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

int Targetable(ITEM_INFO *item, AI_INFO *info)
{
	GAME_VECTOR start, target;
	CREATURE_INFO *creature;
	ITEM_INFO *enemy;

	creature = (CREATURE_INFO *)item->data;
	enemy = creature->enemy;

	/* Don't shoot dead people */
	if (enemy->hit_points <= 0)
		return 0;

	if (info->ahead && info->distance < PEOPLE_SHOOT_RANGE)
	{
		start.x = item->pos.x_pos;
		start.y = item->pos.y_pos - STEP_L*3;
		start.z = item->pos.z_pos;
		start.room_number = item->room_number;

		target.x = enemy->pos.x_pos;
		target.y = enemy->pos.y_pos - STEP_L*3;
		target.z = enemy->pos.z_pos;

		return (LOS(&start, &target));
	}

	return (0);
}

void ControlGlow(sint16 nFX)
	{
	FX_INFO* pFX=effects+nFX;
	if (--pFX->counter)
		{
		pFX->shade+=pFX->speed;
		pFX->frame_number+=pFX->fallspeed;
		}
	else
		KillEffect(nFX);
	}

void Glow(sint32 x,sint32 y,sint32 z,sint16 nRoom,sint16 nFrames,sint16 nShade,sint16 nShadeDelta,sint16 nScale,sint16 nScaleDelta,sint32 dwFlags)
	{
	sint16 nFX;
	FX_INFO* pFX;

	nFX=CreateEffect(nRoom);
	if (nFX!=NO_ITEM)
		{
		pFX=effects+nFX;
		pFX->object_number=GLOW;
		pFX->pos.x_pos=x;
		pFX->pos.y_pos=y;
		pFX->pos.z_pos=z;
		pFX->counter=nFrames;
		pFX->shade=nShade;
		pFX->speed=nShadeDelta;
		pFX->frame_number=nScale;
		pFX->fallspeed=nScaleDelta;
		dwFlags|=SPRITE_ABS|SPRITE_SEMITRANS;
		pFX->pos.x_rot=dwFlags&0xffff;
		pFX->pos.y_rot=(sint16)(dwFlags>>16);
		}
	}


void ControlGunShot(sint16 fx_number)
{
	FX_INFO *fx;

	fx = &effects[fx_number];
	fx->counter--;

	if (!fx->counter)
	{
		KillEffect(fx_number);
		return;
	}

	fx->pos.z_rot =(sint16) GetRandomControl();
	AddDynamicLight(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, 12, 11);
}


sint16 GunShot(sint32 x, sint32 y, sint32 z, sint16 speed, PHD_ANGLE yrot, sint16 room_number)
{
	/* Gun flash from baddies guns */
	sint16 fx_number;
	FX_INFO *fx;

#ifdef PSX_VERSION
	Glow(x,y,z,room_number,4,0x800,0x400,0x200,0,SPRITE_TRANS_ADD|SPRITE_SCALE|SPRITE_SHADE|SPRITE_TINT|SPRITE_COLOUR(0x7f,0x70,0x1f));
#endif

	fx_number = CreateEffect(room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->pos.x_pos = x;
		fx->pos.y_pos = y;
		fx->pos.z_pos = z;
		fx->room_number = room_number;	// OI!  Gav!!!  This is filled in by CreateEffect - Dude
		fx->pos.x_rot = fx->pos.z_rot = 0;
		fx->pos.y_rot = yrot;
		fx->counter = 3;
		fx->frame_number = 0;
		fx->object_number = GUN_FLASH;
		fx->shade = 16*256;				// OI!  Gav!!!  This is filled in by CreateEffect - Dude
	}

	return (fx_number);
}


sint16 GunHit(sint32 x, sint32 y, sint32 z, sint16 speed, PHD_ANGLE yrot, sint16 room_number)
{
	/* Lara gets a blood splat */
	PHD_VECTOR pos;

	/* Give Lara a blood splat on a random mesh to keep her happy */
	pos.x = pos.y = pos.z = 0;
	GetJointAbsPosition(lara_item, &pos, GetRandomControl()*25/0x7fff);
	DoBloodSplat(pos.x, pos.y, pos.z, lara_item->speed, lara_item->pos.y_rot, lara_item->room_number);
	SoundEffect(50, &lara_item->pos, 0);

	return (GunShot(x, y, z, speed, yrot, room_number));
}


sint16 GunMiss(sint32 x, sint32 y, sint32 z, sint16 speed, PHD_ANGLE yrot, sint16 room_number)
{
	/* Lara sees a richochet */
	GAME_VECTOR pos;

	/* Do richochet near Lara's feet	*/
	pos.x = lara_item->pos.x_pos + (GetRandomDraw()-0x4000) * (WALL_L/2)/0x7fff;
	pos.y = lara_item->floor;
	pos.z = lara_item->pos.z_pos + (GetRandomDraw()-0x4000) * (WALL_L/2)/0x7fff;
	pos.room_number = lara_item->room_number;
	Richochet(&pos);

	return (GunShot(x, y, z, speed, yrot, room_number));
}


int ShotLara(ITEM_INFO *item, AI_INFO *info, BITE_INFO *gun, sint16 extra_rotation, int damage)
{
	/* See if gun shot hit Lara given the range.
		Return whether Lara was targetable or not (NOT whether she was hit) */
	int hit, targetable, random, distance;
	sint16 fx_number, smash_item;
	GAME_VECTOR start, target;
	CREATURE_INFO *creature;
	ITEM_INFO *enemy;

	creature = (CREATURE_INFO *)item->data;
	enemy = creature->enemy;

	if (info->distance > PEOPLE_SHOOT_RANGE || !Targetable(item, info))
		hit = targetable = 0;
	else
	{
		/* Add enemy's sideways speed to the distance to give less chance of hitting her if she is moving across line of fire */
		distance = (phd_sin(info->enemy_facing) * enemy->speed >> W2V_SHIFT) * PEOPLE_SHOOT_RANGE / PEOPLE_TARGETING_SPEED;
		distance = info->distance + SQUARE(distance);
		if (distance > PEOPLE_SHOOT_RANGE)
			hit = 0;
		else
		{
			/* Min hit chance is 0x1000, max is 0x6000 */
			random = (PEOPLE_SHOOT_RANGE - info->distance) / (PEOPLE_SHOOT_RANGE/0x5000) + PEOPLE_HIT_CHANCE;
			hit = (GetRandomControl() < random);
		}
		targetable = 1;
	}

	fx_number = NO_ITEM;
	if (enemy == lara_item)
	{
		/* Shooting at Lara */
		if (hit)
		{
			fx_number = CreatureEffect(item, gun, GunHit);

			lara_item->hit_points -= damage;
			lara_item->hit_status = 1;
		}
		else if (targetable)
			fx_number = CreatureEffect(item, gun, GunMiss);
	}
	else
	{
		/* Shooting at Monk */
		fx_number = CreatureEffect(item, gun, GunShot);
		if (hit)
		{
			enemy->hit_points -= damage/10;
			enemy->hit_status = 1;
		}
	}

	if (fx_number != NO_ITEM)
		effects[fx_number].pos.y_rot += extra_rotation;

	/* Smash any objects that get in the way */
	start.x = item->pos.x_pos;
	start.y = item->pos.y_pos - STEP_L*3;
	start.z = item->pos.z_pos;
	start.room_number = item->room_number;

	target.x = enemy->pos.x_pos;
	target.y = enemy->pos.y_pos - STEP_L*3;
	target.z = enemy->pos.z_pos;

	smash_item = ObjectOnLOS(&start, &target);
	if (smash_item != NO_ITEM)
		SmashItem(smash_item, 0);

	return (targetable);
}


void InitialiseCult1(sint16 item_number)
{
	/* Random chance of having a pony tail */
	if (GetRandomControl() < 0x4000)
		items[item_number].mesh_bits -= 0x30;

	/* Always turn off coat tails for CULT1C */
	if (items[item_number].object_number == CULT1C)
		items[item_number].mesh_bits -= 0x1f8000;
}


void Cult1Control(sint16 item_number)
{
	/* The masked venice goon */
	ITEM_INFO *item;
	CREATURE_INFO *cult;
	sint16 angle, head, tilt, random;
	AI_INFO info;

	if (!CreatureActive(item_number))
		return;

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

	/* Has cult member been killed? */
	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != CULT1_DEATH)
		{
			item->anim_number = objects[CULT1].anim_index + CULT1_DIE_ANIM + (GetRandomControl()/0x4000);
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = CULT1_DEATH;
		}
	}
	else
	{
		CreatureAIInfo(item, &info);

		CreatureMood(item, &info, TIMID);

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

		switch (item->current_anim_state)
		{
		case CULT1_STOP:
			/* Single frame that automatically goes into CULT1_WAIT1 */
			cult->maximum_turn = 0;

			if (item->required_anim_state)
				item->goal_anim_state = item->required_anim_state;
			break;

		case CULT1_WAIT1:
			/* This is the true stop state */
			if (cult->mood == ESCAPE_MOOD)
			{
				item->required_anim_state = CULT1_RUN;
				item->goal_anim_state = CULT1_STOP;
			}
			else if (Targetable(item, &info))
			{
				item->required_anim_state = (GetRandomControl() < 0x4000)? CULT1_AIM1 : CULT1_AIM3;
				item->goal_anim_state = CULT1_STOP;
			}
			else if (cult->mood == BORED_MOOD && info.ahead)
			{
				random = (sint16)GetRandomControl();
				if (random < CULT1_POSE_CHANCE)
				{
					item->required_anim_state = CULT1_WAIT2;
					item->goal_anim_state = CULT1_STOP;
				}
				else if (random < CULT1_WALK_CHANCE)
				{
					item->required_anim_state = CULT1_WALK;
					item->goal_anim_state = CULT1_STOP;
				}
			}
			else if (info.distance < CULT1_RUN_RANGE || cult->mood == BORED_MOOD)
			{
				item->required_anim_state = CULT1_WALK;
				item->goal_anim_state = CULT1_STOP;
			}
			else
			{
				item->required_anim_state = CULT1_RUN;
				item->goal_anim_state = CULT1_STOP;
			}
			break;

		case CULT1_WAIT2:
			if (Targetable(item, &info))
			{
				item->goal_anim_state = CULT1_STOP;
				item->required_anim_state = CULT1_AIM1;
			}
			else if (cult->mood != BORED_MOOD || GetRandomControl() < CULT1_UNPOSE_CHANCE || !info.ahead)
				item->goal_anim_state = CULT1_STOP;
			break;

		case CULT1_WALK:
			cult->maximum_turn = CULT1_WALK_TURN;

			if (cult->mood == ESCAPE_MOOD)
				item->goal_anim_state = CULT1_RUN;
			else if (Targetable(item, &info))
			{
				item->required_anim_state = (GetRandomControl()<0x4000)? CULT1_AIM1 : CULT1_AIM3;
				item->goal_anim_state = CULT1_STOP;
			}
			else if (info.distance > CULT1_RUN_RANGE || !info.ahead)
				item->goal_anim_state = CULT1_RUN;
			else if (cult->mood == BORED_MOOD && info.ahead && GetRandomControl() < CULT1_UNWALK_CHANCE)
				item->goal_anim_state = CULT1_STOP;
			break;

		case CULT1_RUN:
			cult->maximum_turn = CULT1_RUN_TURN;
			tilt = angle/4;
			cult->flags = 0;

			if (cult->mood == ESCAPE_MOOD)
			{
				if (Targetable(item, &info))
					item->goal_anim_state = CULT1_SHOOT2;
			}
			else if (Targetable(item, &info))
			{
				if (info.distance < CULT1_RUN_RANGE || info.zone_number != info.enemy_zone)
					item->goal_anim_state = CULT1_STOP;
				else
					item->goal_anim_state = CULT1_SHOOT2;
			}
			else if (cult->mood == BORED_MOOD)
				item->goal_anim_state = CULT1_STOP;
			break;

		case CULT1_AIM1:
		case CULT1_AIM3:
			cult->flags = 0;
			if (info.ahead)
				head = info.angle;

			if (cult->mood == ESCAPE_MOOD)
				item->goal_anim_state = CULT1_STOP;
			else if (Targetable(item, &info))
				item->goal_anim_state = (item->current_anim_state==CULT1_AIM1)? CULT1_SHOOT1 : CULT1_SHOOT3;
			else
				item->goal_anim_state = CULT1_STOP;
			break;

		case CULT1_SHOOT1:
		case CULT1_SHOOT3:
			if (info.ahead)
				head = info.angle;

			if (!cult->flags)
			{
				ShotLara(item, &info, &cult1_gun, head, CULT1_SHOT_DAMAGE);
				cult->flags = 1;
			}
			break;

		case CULT1_SHOOT2:
			if (info.ahead)
				head = info.angle;

			if (!item->required_anim_state)
			{
				if (!ShotLara(item, &info, &cult1_gun, head, CULT1_SHOT_DAMAGE))
					// break out of shoot mode if Lara wasn't targetable
					item->goal_anim_state = CULT1_RUN;

				item->required_anim_state = CULT1_SHOOT2;
			}
			break;
		}
	}

	CreatureTilt(item, tilt);
	CreatureHead(item, head);

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


void InitialiseCult3(sint16 item_number)
{
	/* Cult boss starts in wait anim */
	ITEM_INFO *item;

	InitialiseCreature(item_number);

	item = &items[item_number];
	item->anim_number = objects[CULT3].anim_index + CULT3_WAIT_ANIM;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = item->goal_anim_state = CULT3_WAIT;
}

void Cult3Control(sint16 item_number)
{
	/* The cult boss for the rig; he drops a key when killed */
	ITEM_INFO *item;
	CREATURE_INFO *cult;
	sint16 angle, tilt;
	sint16 head, left, right, body;
	AI_INFO info;
	OBJECT_INFO *obj;

	if (!CreatureActive(item_number))
		return;

	item = &items[item_number];
	cult = (CREATURE_INFO *)item->data;
	angle = tilt = 0;
	head = left = right = body = 0;

	/* Has cult member been killed? */
	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != CULT3_DEATH)
		{
			item->anim_number = objects[CULT3].anim_index + CULT3_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = CULT3_DEATH;
		}
	}
	else
	{
		CreatureAIInfo(item, &info);

		CreatureMood(item, &info, VIOLENT);

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

		switch (item->current_anim_state)
		{
		case CULT3_WAIT:
		case CULT3_STOP:
			/* Stand coolly holding weapons crossed; do this before Lara gets close and when she is dead */
			if (info.ahead)
				head = info.angle;

			if (cult->mood == BORED_MOOD && lara_item->hit_points <= 0)
				item->goal_anim_state = CULT3_WAIT;
			else if (Targetable(item, &info))
			{
				if (info.distance > CULT3_STOP_RANGE)
					item->goal_anim_state = CULT3_WALK;
				else
					item->goal_anim_state = CULT3_AIM2;
			}
			else if (cult->mood == ESCAPE_MOOD)
				item->goal_anim_state = CULT3_RUN;
			else if (cult->mood == ATTACK_MOOD)
			{
				if (info.distance > CULT3_RUN_RANGE || !info.ahead)
					item->goal_anim_state = CULT3_RUN;
				else
					item->goal_anim_state = CULT3_WALK;
			}
			else if (cult->mood == STALK_MOOD || !info.ahead)
				item->goal_anim_state = CULT3_WALK;
			break;

		case CULT3_WALK:
			cult->maximum_turn = CULT3_WALK_TURN;
			if (info.ahead)
				head = info.angle;

			if (Targetable(item, &info))
			{
				if (info.distance < CULT3_STOP_RANGE || info.zone_number != info.enemy_zone)
					item->goal_anim_state = CULT3_STOP;
				else if (info.angle < 0)
					item->goal_anim_state = CULT3_AIML;
				else
					item->goal_anim_state = CULT3_AIMR;
			}
			else if (cult->mood == ESCAPE_MOOD)
				item->goal_anim_state = CULT3_RUN;
			else if (cult->mood == STALK_MOOD || cult->mood == ATTACK_MOOD)
			{
				if (info.distance > CULT3_RUN_RANGE || !info.ahead)
					item->goal_anim_state = CULT3_RUN;
			}
			else if (lara_item->hit_points <= 0) // BORED_MOOD
				item->goal_anim_state = CULT3_WAIT;
			else if (info.ahead)
				item->goal_anim_state = CULT3_STOP;
			break;

		case CULT3_RUN:
			cult->maximum_turn = CULT3_RUN_TURN;
			tilt = angle/4;
			if (info.ahead)
				head = info.angle;

			if (Targetable(item, &info))
			{
				if (info.zone_number != info.enemy_zone)
					item->goal_anim_state = CULT3_STOP;
				else if (info.angle < 0)
					item->goal_anim_state = CULT3_AIML;
				else
					item->goal_anim_state = CULT3_AIMR;
			}
			else if (cult->mood == BORED_MOOD)
			{
				if (lara_item->hit_points <= 0)
					item->goal_anim_state = CULT3_WAIT;
				else
					item->goal_anim_state = CULT3_STOP;
			}
			else if (info.ahead && info.distance < CULT3_RUN_RANGE)
				item->goal_anim_state = CULT3_WALK;
			break;

		case CULT3_AIM2:
			cult->flags = 0;
			if (info.ahead)
				body = info.angle;

			if (Targetable(item, &info))
				item->goal_anim_state = CULT3_SHOOT2;
			else
				item->goal_anim_state = CULT3_STOP;
			break;

		case CULT3_AIML:
			cult->flags = 0;
			if (info.ahead)
				left = head = info.angle;

			if (Targetable(item, &info))
				item->goal_anim_state = CULT3_SHOOTL;
			else
				item->goal_anim_state = CULT3_WALK;
			break;

		case CULT3_AIMR:
			cult->flags = 0;
			if (info.ahead)
				right = head = info.angle;

			if (Targetable(item, &info))
				item->goal_anim_state = CULT3_SHOOTR;
			else
				item->goal_anim_state = CULT3_WALK;
			break;

		case CULT3_SHOOT2:
			if (info.ahead)
				body = info.angle;

			if (!cult->flags)
			{
				ShotLara(item, &info, &cult3_gunl, head, CULT3_SHOT_DAMAGE);
				ShotLara(item, &info, &cult3_gunr, head, CULT3_SHOT_DAMAGE);
				cult->flags = 1;
			}
			break;

		case CULT3_SHOOTL:
			if (info.ahead)
				left = head = info.angle;

			if (!cult->flags)
			{
				ShotLara(item, &info, &cult3_gunl, head, CULT3_SHOT_DAMAGE);
				cult->flags = 1;
			}
			break;

		case CULT3_SHOOTR:
			if (info.ahead)
				right = head = info.angle;

			if (!cult->flags)
			{
				ShotLara(item, &info, &cult3_gunr, head, CULT3_SHOT_DAMAGE);
				cult->flags = 1;
			}
			break;
		}
	}

	CreatureTilt(item, tilt);

	/* Set bone rotations according to which parts have been activated */
	obj = &objects[CULT3];
	if (body)
		bones[obj->bone_index] |= ROT_Y;
	else
		bones[obj->bone_index] &= (~ROT_Y);

	if (left)
		bones[obj->bone_index + 2*4] |= ROT_Y;
	else
		bones[obj->bone_index + 2*4] &= (~ROT_Y);

	if (right)
		bones[obj->bone_index + 6*4] |= ROT_Y;
	else
		bones[obj->bone_index + 6*4] &= (~ROT_Y);

	if (head)
		bones[obj->bone_index + 10*4] |= ROT_Y;
	else
		bones[obj->bone_index + 10*4] &= (~ROT_Y);

	/* Then choose which ones to rotate... */
	if (body)
		CreatureHead(item, body);
	else if (left)
	{
		CreatureHead(item, left);
		CreatureNeck(item, head);
	}
	else if (right)
	{
		CreatureHead(item, right);
		CreatureNeck(item, head);
	}
	else if (head)
		CreatureHead(item, head);

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


void Worker1Control(sint16 item_number)
{
	/* Rig worker */
	ITEM_INFO *item;
	CREATURE_INFO *worker;
	sint16 angle, head, neck, tilt;
	AI_INFO info;

	if (!CreatureActive(item_number))
		return;

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

	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != WORK1_DEATH)
		{
			item->anim_number = objects[WORKER1].anim_index + WORK1_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = WORK1_DEATH;
		}
	}
	else
	{
		CreatureAIInfo(item, &info);

		CreatureMood(item, &info, TIMID);

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

		switch (item->current_anim_state)
		{
		case WORK1_STOP:
			if (info.ahead)
				neck = info.angle;

			worker->flags = 0;
			worker->maximum_turn = 0;

			if (worker->mood == ESCAPE_MOOD)
				item->goal_anim_state = WORK1_RUN;
			else if (Targetable(item, &info))
			{
				if (info.distance < WORK1_SHOOT1_RANGE || info.zone_number != info.enemy_zone)
				{
					if (GetRandomControl() < 0x4000)
						item->goal_anim_state = WORK1_AIM;
					else
						item->goal_anim_state = WORK1_AIM2;
				}
				else
					item->goal_anim_state = WORK1_WALK;
			}
			else if (worker->mood == BORED_MOOD && info.ahead)
				item->goal_anim_state = WORK1_WAIT;
			else if (info.distance > WORK1_RUN_RANGE)
				item->goal_anim_state = WORK1_RUN;
			else
				item->goal_anim_state = WORK1_WALK;
			break;

		case WORK1_WAIT:
			if (info.ahead)
				neck = info.angle;

			if (Targetable(item, &info))
				item->goal_anim_state = WORK1_SHOOT1;
			else if (worker->mood != BORED_MOOD || !info.ahead)
				item->goal_anim_state = WORK1_STOP;
			break;

		case WORK1_WALK:
			if (info.ahead)
				neck = info.angle;

			worker->flags = 0;
			worker->maximum_turn = WORK1_WALK_TURN;

			if (worker->mood == ESCAPE_MOOD)
				item->goal_anim_state = WORK1_RUN;
			else if (Targetable(item, &info))
			{
				if (info.distance < WORK1_SHOOT1_RANGE || info.zone_number != info.enemy_zone)
					item->goal_anim_state = WORK1_STOP;
				else
					item->goal_anim_state = WORK1_SHOOT2;
			}
			else if (worker->mood == BORED_MOOD && info.ahead)
				item->goal_anim_state = WORK1_STOP;
			else if (info.distance > WORK1_RUN_RANGE)
				item->goal_anim_state = WORK1_RUN;
			break;

		case WORK1_RUN:
			if (info.ahead)
				neck = info.angle;

			worker->maximum_turn = WORK1_RUN_TURN;
			tilt = angle/2;

			if (worker->mood == ESCAPE_MOOD)
				break;
			else if (Targetable(item, &info))
				item->goal_anim_state = WORK1_WALK;
			else if (worker->mood == BORED_MOOD || worker->mood == STALK_MOOD)
				item->goal_anim_state = WORK1_WALK;
			break;

		case WORK1_AIM2:
			if (Targetable(item, &info))
				item->goal_anim_state = WORK1_SHOOT3;
		case WORK1_AIM:
			worker->flags = 0;

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

		case WORK1_SHOOT1:
		case WORK1_SHOOT3:
			if (info.ahead)
				head = info.angle;

			if (!worker->flags)
			{
				ShotLara(item, &info, &work1_gun, head, WORK1_SHOT_DAMAGE);
				worker->flags = 1;
			}

			if (item->goal_anim_state != WORK1_STOP)
			{
				if (worker->mood == ESCAPE_MOOD || info.distance > WORK1_SHOOT1_RANGE || !Targetable(item, &info))
					item->goal_anim_state = WORK1_STOP;
			}
			break;

		case WORK1_SHOOT2:
			if (info.ahead)
				head = info.angle;

			if (!worker->flags)
			{
				ShotLara(item, &info, &work1_gun, head, WORK1_SHOT_DAMAGE);
				worker->flags = 1;
			}
			break;
		}
	}

	CreatureTilt(item, tilt);
	CreatureHead(item, head);
	CreatureNeck(item, neck);

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

sint16 DragonFire(sint32 x, sint32 y, sint32 z, sint16 speed, PHD_ANGLE yrot, sint16 room_number);

void Worker2Control(sint16 item_number)
{
	/* Rig worker II */
	ITEM_INFO *item;
	CREATURE_INFO *worker;
	sint16 angle, head, neck, tilt;
	AI_INFO info;

	if (!CreatureActive(item_number))
		return;

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

	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != WORK2_DEATH)
		{
			item->anim_number = objects[item->object_number].anim_index + WORK2_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = WORK2_DEATH;
		}
	}
	else
	{
		CreatureAIInfo(item, &info);

		CreatureMood(item, &info, TIMID);

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

		switch (item->current_anim_state)
		{
		case WORK2_STOP:
			if (info.ahead)
				neck = info.angle;

			worker->flags = 0;
			worker->maximum_turn = 0;

			if (worker->mood == ESCAPE_MOOD)
				item->goal_anim_state = WORK2_RUN;
			else if (Targetable(item, &info))
			{
				if (info.distance < WORK2_SHOOT1_RANGE || info.zone_number != info.enemy_zone)
				{
					if (item->object_number == WORKER4 || GetRandomControl() < 0x4000)
						item->goal_anim_state = WORK2_AIM1;
					else
						item->goal_anim_state = WORK2_AIM3;
				}
				else
					item->goal_anim_state = WORK2_WALK;
			}
			else if (worker->mood == BORED_MOOD && info.ahead)
				item->goal_anim_state = WORK2_WAIT;
			else if (info.distance > WORK2_RUN_RANGE)
				item->goal_anim_state = WORK2_RUN;
			else
				item->goal_anim_state = WORK2_WALK;
			break;

		case WORK2_WAIT:
			if (info.ahead)
				neck = info.angle;

			if (Targetable(item, &info))
				item->goal_anim_state = WORK2_SHOOT1;
			else if (worker->mood != BORED_MOOD || !info.ahead)
				item->goal_anim_state = WORK2_STOP;
			break;

		case WORK2_WALK:
			if (info.ahead)
				neck = info.angle;

			worker->flags = 0;
			worker->maximum_turn = WORK2_WALK_TURN;

			if (worker->mood == ESCAPE_MOOD)
				item->goal_anim_state = WORK2_RUN;
			else if (Targetable(item, &info))
			{
				if (info.distance < WORK2_SHOOT1_RANGE || info.zone_number != info.enemy_zone)
					item->goal_anim_state = WORK2_STOP;
				else
					item->goal_anim_state = WORK2_AIM2;
			}
			else if (worker->mood == BORED_MOOD && info.ahead)
				item->goal_anim_state = WORK2_STOP;
			else if (info.distance > WORK2_RUN_RANGE)
				item->goal_anim_state = WORK2_RUN;
			break;

		case WORK2_RUN:
			if (info.ahead)
				neck = info.angle;

			worker->maximum_turn = WORK2_RUN_TURN;
			tilt = angle/2;

			if (worker->mood == ESCAPE_MOOD)
				break;
			else if (Targetable(item, &info))
				item->goal_anim_state = WORK2_WALK;
			else if (worker->mood == BORED_MOOD || worker->mood == STALK_MOOD)
				item->goal_anim_state = WORK2_WALK;
			break;

		case WORK2_AIM1:
		case WORK2_AIM3:
			worker->flags = 0;

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

				if (Targetable(item, &info))
					item->goal_anim_state = (item->current_anim_state==WORK2_AIM1)? WORK2_SHOOT1 : WORK2_SHOOT3;
				else
					item->goal_anim_state = WORK2_STOP;
			}
			break;

		case WORK2_AIM2:
			worker->flags = 0;

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

				if (Targetable(item, &info))
					item->goal_anim_state = WORK2_SHOOT2;
				else
					item->goal_anim_state = WORK2_WALK;
			}
			break;


		case WORK2_SHOOT3:
			if (item->goal_anim_state != WORK2_STOP)
			{
				if (worker->mood == ESCAPE_MOOD || info.distance > WORK2_SHOOT1_RANGE || !Targetable(item, &info))
					item->goal_anim_state = WORK2_STOP;
			}
		case WORK2_SHOOT2:
		case WORK2_SHOOT1:
			if (info.ahead)
				head = info.angle;

			if (item->object_number == WORKER2)
			{
				// WORKER2: guy with machine gun
				if (!worker->flags)
				{
					ShotLara(item, &info, &work2_gun, head, WORK2_SHOT_DAMAGE);
					worker->flags = 5;
				}
				else
					worker->flags--;
			}
			else
				// WORKER4: guy with flamethrower
				CreatureEffect(item, &work2_gun, DragonFire);
			break;
		}
	}

	CreatureTilt(item, tilt);
	CreatureHead(item, head);
	CreatureNeck(item, neck);

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


void BanditControl(sint16 item_number)
{
	/* Tibet Goon 1: Uzi mother */
	ITEM_INFO *item;
	CREATURE_INFO *bandit;
	sint16 angle, head, neck, tilt;
	int random;
	AI_INFO info;

	if (!CreatureActive(item_number))
		return;

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

	if (item->hit_points <= 0)
	{
		item->hit_points = 0;
		if (item->current_anim_state != BANDIT_DEATH)
		{
			item->anim_number = objects[BANDIT1].anim_index + BANDIT_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BANDIT_DEATH;
		}
	}
	else
	{
		CreatureAIInfo(item, &info);

		CreatureMood(item, &info, TIMID);

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

		switch (item->current_anim_state)
		{
		case BANDIT_WAIT:
			if (info.ahead)
				neck = info.angle;

			bandit->maximum_turn = 0;

			if (bandit->mood == ESCAPE_MOOD)
				item->goal_anim_state = BANDIT_RUN;
			else if (Targetable(item, &info))
			{
				if (info.distance > BANDIT_WALK_RANGE)
					item->goal_anim_state = BANDIT_WALK;
				else
				{
					// 3 different shooting stances to choose from!
					random = GetRandomControl();
					if (random < 0x2000)
						item->goal_anim_state = BANDIT_SHOOT1;
					else if (random < 0x4000)
						item->goal_anim_state = BANDIT_SHOOT2;
					else
						item->goal_anim_state = BANDIT_AIM3;
				}
			}
			else if (bandit->mood == BORED_MOOD)
			{
				if (info.ahead)
					item->goal_anim_state = BANDIT_WAIT;
				else
					item->goal_anim_state = BANDIT_WALK;
			}
			else
				item->goal_anim_state = BANDIT_RUN;
			break;

		case BANDIT_WALK:
			if (info.ahead)
				neck = info.angle;

			bandit->maximum_turn = BANDIT_WALK_TURN;

			if (bandit->mood == ESCAPE_MOOD)
				item->goal_anim_state = BANDIT_RUN;
			else if (Targetable(item, &info))
			{
				if (info.distance > BANDIT_WALK_RANGE && info.zone_number == info.enemy_zone)
					item->goal_anim_state = BANDIT_AIM4;
				else
					item->goal_anim_state = BANDIT_WAIT;
			}
			else if (bandit->mood == BORED_MOOD)
			{
				if (info.ahead)
					item->goal_anim_state = BANDIT_WALK;
				else
					item->goal_anim_state = BANDIT_WAIT;
			}
			else
				item->goal_anim_state = BANDIT_RUN;
			break;

		case BANDIT_RUN:
			if (info.ahead)
				neck = info.angle;

			bandit->maximum_turn = BANDIT_RUN_TURN;
			tilt = angle/2;

			if (bandit->mood == ESCAPE_MOOD)
				break;
			else if (Targetable(item, &info))
				item->goal_anim_state = BANDIT_WAIT;
			else if (bandit->mood == BORED_MOOD)
				item->goal_anim_state = BANDIT_WALK;
			break;

		case BANDIT_SHOOT1:
		case BANDIT_SHOOT2:
		case BANDIT_SHOOT3A:
		case BANDIT_SHOOT3B:
			if (info.ahead)
				head = info.angle;

			if (!ShotLara(item, &info, &bandit_gun, head, BANDIT_SHOT_DAMAGE))
				item->goal_anim_state = BANDIT_WAIT;
			break;

		case BANDIT_SHOOT4A:
		case BANDIT_SHOOT4B:
			if (info.ahead)
				head = info.angle;

			if (!ShotLara(item, &info, &bandit_gun, head, BANDIT_SHOT_DAMAGE))
				item->goal_anim_state = BANDIT_WALK;

			/* Go back to walk so Bandit can stop when he gets close enough */
			if (info.distance < BANDIT_WALK_RANGE)
				item->goal_anim_state = BANDIT_WALK;
			break;
		}
	}

	CreatureTilt(item, tilt);
	CreatureHead(item, head);
	CreatureNeck(item, neck);

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


void Bandit2Control(sint16 item_number)
{
	/* Tibet Goon 2: balaclava man */
	ITEM_INFO *item;
	CREATURE_INFO *bandit;
	sint16 angle, head, neck, tilt;
	int random;
	AI_INFO info;

	if (!CreatureActive(item_number))
		return;

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

	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != BANDIT2_DEATH)
		{
			item->anim_number = objects[BANDIT2].anim_index + BANDIT2_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BANDIT2_DEATH;
		}
	}
	else
	{
		CreatureAIInfo(item, &info);

		CreatureMood(item, &info, VIOLENT);

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

		switch (item->current_anim_state)
		{
		case BANDIT2_WAIT:
			if (info.ahead)
				neck = info.angle;

			bandit->maximum_turn = 0;

			if (bandit->mood == ESCAPE_MOOD)
				item->goal_anim_state = BANDIT2_RUN;
			else if (Targetable(item, &info))
			{
				if (info.distance > BANDIT2_WALK_RANGE && GetRandomControl() < 0x4000)
					item->goal_anim_state = BANDIT2_WALK;
				else
				{
					random = GetRandomControl();
					if (random < 0x2000)
						item->goal_anim_state = BANDIT2_SHOOT1;
					else if (random < 0x5000)
						item->goal_anim_state = BANDIT2_SHOOT2;
					else
						item->goal_anim_state = BANDIT2_AIM5;
				}
			}
			else if (bandit->mood == BORED_MOOD)
			{
				if (!info.ahead || GetRandomControl() < 0x100)
					item->goal_anim_state = BANDIT2_WALK;
			}
			else
				item->goal_anim_state = BANDIT2_RUN;
			break;

		case BANDIT2_WALK:
			if (info.ahead)
				neck = info.angle;

			bandit->maximum_turn = BANDIT2_WALK_TURN;

			if (bandit->mood == ESCAPE_MOOD)
				item->goal_anim_state = BANDIT2_RUN;
			else if (Targetable(item, &info))
			{
				if (info.distance < BANDIT2_WALK_RANGE || info.zone_number == info.enemy_zone || GetRandomControl() < 0x400)
					item->goal_anim_state = BANDIT2_WAIT;
				else
					item->goal_anim_state = BANDIT2_AIM4;
			}
			else if (bandit->mood == BORED_MOOD)
			{
				if (info.ahead && GetRandomControl() < 0x400)
					item->goal_anim_state = BANDIT2_WAIT;
			}
			else
				item->goal_anim_state = BANDIT2_RUN;
			break;

		case BANDIT2_RUN:
			if (info.ahead)
				neck = info.angle;

			bandit->maximum_turn = BANDIT2_RUN_TURN;
			tilt = angle/2;

			if (bandit->mood == ESCAPE_MOOD)
				break;
			else if (bandit->mood == BORED_MOOD || Targetable(item, &info))
				item->goal_anim_state = BANDIT2_WAIT;
			break;

		case BANDIT2_AIM1:
		case BANDIT2_AIM2:
		case BANDIT2_AIM4:
			if (info.ahead)
				head = info.angle;

			bandit->flags = 0;
			break;

		case BANDIT2_AIM5:
			if (info.ahead)
				head = info.angle;

			bandit->flags = 0;
			if (Targetable(item, &info))
				item->goal_anim_state = BANDIT2_SHOOT5;
			else
				item->goal_anim_state = BANDIT2_WAIT;
			break;

		case BANDIT2_SHOOT1:
		case BANDIT2_SHOOT2:
		case BANDIT2_SHOOT5:
			if (info.ahead)
				head = info.angle;

			/* Required state is set after this so only one shot is fired */
			if (!bandit->flags)
			{
				if (!ShotLara(item, &info, &bandit2_gun, head, BANDIT2_SHOT_DAMAGE) || GetRandomControl()<0x2000)
					item->goal_anim_state = BANDIT2_WAIT;

				bandit->flags = 1;
			}
			break;

		case BANDIT2_SHOOT4A:
			if (info.ahead)
				head = info.angle;

			if (bandit->flags != 1)
			{
				if (!ShotLara(item, &info, &bandit2_gun, head, BANDIT2_SHOT_DAMAGE))
					item->goal_anim_state = BANDIT2_WALK;
				bandit->flags = 1;
			}

			/* Go back to walk so Bandit can stop when he gets close enough */
			if (info.distance < BANDIT2_WALK_RANGE)
				item->goal_anim_state = BANDIT2_WALK;
			break;

		case BANDIT2_SHOOT4B:
			if (info.ahead)
				head = info.angle;

			if (bandit->flags != 2)
			{
				if (!ShotLara(item, &info, &bandit2_gun, head, BANDIT2_SHOT_DAMAGE))
					item->goal_anim_state = BANDIT2_WALK;
				bandit->flags = 2;
			}

			/* Go back to walk so Bandit can stop when he gets close enough */
			if (info.distance < BANDIT2_WALK_RANGE)
				item->goal_anim_state = BANDIT2_WALK;
			break;
		}
	}

	CreatureTilt(item, tilt);
	CreatureHead(item, head);
	CreatureNeck(item, neck);

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


enum winston_anim {WINSTON_EMPTY, WINSTON_STOP, WINSTON_WALK};

#define WINSTON_STOP_RANGE SQUARE(WALL_L*3/2)

void WinstonControl(sint16 item_number)
{
	ITEM_INFO *item;
	CREATURE_INFO *winston;
	sint16 angle;
	AI_INFO info;

	if (!CreatureActive(item_number))
		return;

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

	CreatureAIInfo(item, &info);
	CreatureMood(item, &info, VIOLENT); // ha ha

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

	if (item->current_anim_state == WINSTON_STOP)
	{
		if (info.distance > WINSTON_STOP_RANGE || !info.ahead)
		{
			if (item->goal_anim_state != WINSTON_WALK)
			{
				item->goal_anim_state = WINSTON_WALK;
				SoundEffect(345, &item->pos, 0); // huff
			}
		}
	}
	else
	{
		if (info.distance < WINSTON_STOP_RANGE)
		{
			if (info.ahead)
			{
				item->goal_anim_state = WINSTON_STOP;
				if (winston->flags & 1)
					winston->flags--;
			}
			else if (!(winston->flags&1))
			{
				// scared
				SoundEffect(344, &item->pos, 0);
				SoundEffect(347, &item->pos, 0);
				winston->flags |= 1;
			}
		}
	}

	if (item->touch_bits && !(winston->flags&2))
	{
		// bumped
		SoundEffect(346, &item->pos, 0);
		SoundEffect(347, &item->pos, 0);
		winston->flags |= 2;
	}
	else if (!item->touch_bits && winston->flags&2)
		winston->flags -= 2;

	// tray clink
	if (GetRandomDraw() < 0x100)
		SoundEffect(347, &item->pos, 0);

	CreatureAnimation(item_number, angle, 0);
}
