/*********************************************************************************************/
/*                                                                                           */
/* Hand to Hand Fighter Control                                                              */
/*                                                                                           */
/*********************************************************************************************/

// MONK and WORKER3

#include "game.h"

#define MONK_BIFF_DAMAGE 150
#define MONK_BIFF_ENEMY_DAMAGE 5

#define WORK3_HIT_DAMAGE 80
#define WORK3_SWIPE_DAMAGE 100

#define XIAN_HIT1_DAMAGE 75
#define XIAN_HIT2_DAMAGE 75
#define XIAN_HIT5_DAMAGE 75
#define XIAN_HIT6_DAMAGE 120

#define KNIFE_SPEED  150

#define WARRIOR_HACK_DAMAGE 300

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

// MONK 1
enum monk_anims {MONK_EMPTY, MONK_WAIT1, MONK_WALK, MONK_RUN, MONK_ATTACK1, MONK_ATTACK2, MONK_ATTACK3, MONK_ATTACK4, MONK_AIM3, MONK_DEATH, MONK_ATTACK5, MONK_WAIT2};

#define MONK_WALK_TURN (ONE_DEGREE*3)
#define MONK_RUN_TURN (ONE_DEGREE*4)

#define MONK_CLOSE_RANGE SQUARE(WALL_L/2)
#define MONK_LONG_RANGE SQUARE(WALL_L)
#define MONK_ATTACK5_RANGE SQUARE(WALL_L*3)
#define MONK_WALK_RANGE SQUARE(WALL_L*2)

#define MONK_HIT_RANGE (STEP_L*2)

#define MONK_TOUCH 0x4000

#define MONK_DIE_ANIM 20

int monks_attack_lara = 0;

BITE_INFO monk_hit = {-23,16,265, 14};


// WORKER3: guy with a stick
BITE_INFO work3_hit = {247,10,11, 10};

enum work3_anims {WORK3_EMPTY, WORK3_STOP, WORK3_WALK, WORK3_PUNCH2, WORK3_AIM2, WORK3_WAIT, WORK3_AIM1, WORK3_AIM0, WORK3_PUNCH1, WORK3_PUNCH0, 
	WORK3_RUN, WORK3_DEATH, WORK3_CLIMB3, WORK3_CLIMB1, WORK3_CLIMB2, WORK3_FALL3};

#define WORK3_WALK_TURN (ONE_DEGREE*5)
#define WORK3_RUN_TURN (ONE_DEGREE*6)

#define WORK3_ATTACK0_RANGE SQUARE(WALL_L/2)
#define WORK3_ATTACK1_RANGE SQUARE(WALL_L)
#define WORK3_ATTACK2_RANGE SQUARE(WALL_L*3/2)

#define WORK3_WALK_RANGE SQUARE(WALL_L*2)

#define WORK3_WALK_CHANCE 0x100
#define WORK3_WAIT_CHANCE 0x100

#define WORK3_DIE_ANIM 26

#define WORK3_CLIMB1_ANIM 28
#define WORK3_CLIMB2_ANIM 29
#define WORK3_CLIMB3_ANIM 27
#define WORK3_FALL3_ANIM  30

#define WORK3_TOUCH 0x600

#define WORK3_VAULT_SHIFT 260

// Xian Lord bloke
BITE_INFO xian_left = {0,0,920, 11};
BITE_INFO xian_right = {0,0,920, 18};

enum xian_anims {XIAN_EMPTY, XIAN_STOP, XIAN_STOP2, XIAN_WALK, XIAN_RUN, XIAN_AIM1, XIAN_HIT1, XIAN_AIM2, XIAN_HIT2,
	XIAN_AIM3, XIAN_HIT3, XIAN_AIM4, XIAN_HIT4, XIAN_AIM5, XIAN_HIT5, XIAN_AIM6, XIAN_HIT6, XIAN_DEATH, XIAN_START, XIAN_KILL};

#define XIAN_WALK_TURN (ONE_DEGREE*3)
#define XIAN_RUN_TURN (ONE_DEGREE*5)

#define XIAN_STOP_CHANCE 0x200
#define XIAN_WALK_CHANCE (XIAN_STOP_CHANCE + 0x200)

#define XIAN_ATTACK1_RANGE SQUARE(WALL_L)
#define XIAN_ATTACK2_RANGE SQUARE(WALL_L*3/2)
#define XIAN_ATTACK3_RANGE SQUARE(WALL_L*2)
#define XIAN_ATTACK4_RANGE SQUARE(WALL_L*2)
#define XIAN_ATTACK5_RANGE SQUARE(WALL_L)
#define XIAN_ATTACK6_RANGE SQUARE(WALL_L*2)

#define XIAN_RUN_RANGE SQUARE(WALL_L*3)

#define XIAN_DIE_ANIM 0
#define XIAN_START_ANIM 48
#define XIAN_KILL_ANIM 49

#define XIAN_TOUCHL 0x00800
#define XIAN_TOUCHR 0x40000

// Flying Warrior
BITE_INFO warrior_sword = {0,37,550, 15};

enum warrior_anims {WARRIOR_EMPTY, WARRIOR_STOP, WARRIOR_WALK, WARRIOR_AIM1, WARRIOR_SLASH1, WARRIOR_AIM2, WARRIOR_SLASH2, 
	WARRIOR_WAIT, WARRIOR_FLY, WARRIOR_START, WARRIOR_AIM3, WARRIOR_SLASH3, WARRIOR_DEATH};

#define WARRIOR_WALK_TURN (ONE_DEGREE*5)
#define WARRIOR_FLY_TURN (ONE_DEGREE*4)

#define WARRIOR_ATTACK1_RANGE SQUARE(WALL_L)
#define WARRIOR_ATTACK3_RANGE SQUARE(WALL_L*2)

#define WARRIOR_TOUCH 0x0c000

// Cult 2: the knife thrower
enum cult2_anims {CULT2_EMPTY, CULT2_STOP, CULT2_WALK, CULT2_RUN, CULT2_AIM1L, CULT2_SHOOT1L, CULT2_AIM1R, CULT2_SHOOT1R, 
	CULT2_AIM2, CULT2_SHOOT2, CULT2_DEATH};

#define CULT2_DIE_ANIM 23

#define CULT2_WALK_TURN (ONE_DEGREE*3)
#define CULT2_RUN_TURN  (ONE_DEGREE*6)

#define CULT2_WALK_RANGE SQUARE(WALL_L*4)
#define CULT2_KNIFE_RANGE SQUARE(WALL_L*6)
#define CULT2_STOP_RANGE SQUARE(WALL_L*5/2)

BITE_INFO cult2_left = {0,0,0,5};
BITE_INFO cult2_right = {0,0,0,8};

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

sint16 Knife(sint32 x, sint32 y, sint32 z, sint16 speed, PHD_ANGLE yrot, sint16 room_number)
{
	sint16 fx_number;
	FX_INFO *fx;

	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;
		fx->pos.x_rot = fx->pos.z_rot = 0;
		fx->pos.y_rot = yrot;
		fx->speed = KNIFE_SPEED;
		fx->frame_number = 0;
		fx->object_number = KNIFE;
		fx->shade = 14 * 256;
		ShootAtLara(fx);
	}

	return (fx_number);
}


void Cult2Control(sint16 item_number)
{
	/* Cult2 throws knives at Lara */
	ITEM_INFO *item;
	CREATURE_INFO *cult;
	AI_INFO info;
	sint16 angle, head, neck, tilt;

	if (!CreatureActive(item_number))
		return;

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

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

		CreatureMood(item, &info, TIMID);

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

		switch (item->current_anim_state)
		{
		case CULT2_STOP:
			cult->maximum_turn = 0;
			if (info.ahead)
				neck = info.angle;				

			if (cult->mood == ESCAPE_MOOD)
				item->goal_anim_state = CULT2_RUN;
			else if (Targetable(item, &info))
				item->goal_anim_state = CULT2_AIM2;
			else if (cult->mood == BORED_MOOD)
			{
				if (!info.ahead || info.distance > CULT2_KNIFE_RANGE)
					item->goal_anim_state = CULT2_WALK;
			}
			else if (info.ahead && info.distance < CULT2_WALK_RANGE)
				item->goal_anim_state = CULT2_WALK;
			else
				item->goal_anim_state = CULT2_RUN;
			break;

		case CULT2_WALK:
			cult->maximum_turn = CULT2_WALK_TURN;
			if (info.ahead)
				neck = info.angle;

			if (cult->mood == ESCAPE_MOOD)
				item->goal_anim_state = CULT2_RUN;
			else if (Targetable(item, &info))
			{
				if (info.distance < CULT2_STOP_RANGE || info.zone_number != info.enemy_zone)
					item->goal_anim_state = CULT2_STOP;
				else if (GetRandomControl() < 0x4000)
					item->goal_anim_state = CULT2_AIM1L;
				else
					item->goal_anim_state = CULT2_AIM1R;
			}
			else if (cult->mood == BORED_MOOD)
			{
				if (info.ahead && info.distance < CULT2_KNIFE_RANGE)
					item->goal_anim_state = CULT2_STOP;
			}
			else if (!info.ahead || info.distance > CULT2_WALK_RANGE)
				item->goal_anim_state = CULT2_RUN;
			break;

		case CULT2_RUN:
			cult->maximum_turn = CULT2_RUN_TURN;
			tilt = angle >> 2;
			if (info.ahead)
				neck = info.angle;

			if (cult->mood == ESCAPE_MOOD)
				break;
			else if (Targetable(item, &info))
				item->goal_anim_state = CULT2_WALK;
			else if (cult->mood == BORED_MOOD)
			{
				if (info.ahead && info.distance < CULT2_KNIFE_RANGE)
					item->goal_anim_state = CULT2_STOP;
				else
					item->goal_anim_state = CULT2_WALK;
			}
			else if (info.ahead && info.distance < CULT2_WALK_RANGE)
				item->goal_anim_state = CULT2_WALK;
			break;

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

			if (Targetable(item, &info))
				item->goal_anim_state = CULT2_SHOOT1L;
			else
				item->goal_anim_state = CULT2_WALK;
			break;

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

			if (Targetable(item, &info))
				item->goal_anim_state = CULT2_SHOOT1R;
			else
				item->goal_anim_state = CULT2_WALK;
			break;

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

			if (Targetable(item, &info))
				item->goal_anim_state = CULT2_SHOOT2;
			else
				item->goal_anim_state = CULT2_STOP;
			break;

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

			if (!cult->flags)
			{
				CreatureEffect(item, &cult2_left, Knife);
				cult->flags = 1;
			}
			break;

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

			if (!cult->flags)
			{
				CreatureEffect(item, &cult2_right, Knife);
				cult->flags = 1;
			}
			break;

		case CULT2_SHOOT2:
			if (info.ahead)
				head = info.angle;
			
			if (!cult->flags)
			{
				CreatureEffect(item, &cult2_left, Knife);
				CreatureEffect(item, &cult2_right, Knife);
				cult->flags = 1;
			}
			break;
		}
	}

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

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


void MonkControl(sint16 item_number)
{
	ITEM_INFO *item, *enemy;
	CREATURE_INFO *monk;
	sint16 angle, head, tilt, random;
	AI_INFO info;

	if (!CreatureActive(item_number))
		return;

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

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

		CreatureMood(item, &info, VIOLENT);

		angle = CreatureTurn(item, monk->maximum_turn);
		if (info.ahead)
			head = info.angle;

		switch (item->current_anim_state)
		{
		case MONK_WAIT1:
			monk->flags &= 0x0fff;

			if (!monks_attack_lara && info.ahead && lara.target == item)
				break;
			else if (monk->mood == BORED_MOOD)
				item->goal_anim_state = MONK_WALK;
			else if (monk->mood == ESCAPE_MOOD)
				item->goal_anim_state = MONK_RUN;
			else if (info.ahead && info.distance < MONK_CLOSE_RANGE)
			{
				if (GetRandomControl() < 0x7000)
					item->goal_anim_state = MONK_ATTACK1;
				else
					item->goal_anim_state = MONK_WAIT2;
			}
			else if (info.ahead && info.distance < MONK_LONG_RANGE)
				item->goal_anim_state = MONK_ATTACK4;
			else if (info.ahead && info.distance < MONK_WALK_RANGE)
				item->goal_anim_state = MONK_WALK;
			else
				item->goal_anim_state = MONK_RUN;
			break;

		case MONK_WAIT2:
			monk->flags &= 0x0fff;

			if (!monks_attack_lara && info.ahead && lara.target == item)
				break;
			else if (monk->mood == BORED_MOOD)
				item->goal_anim_state = MONK_WALK;
			else if (monk->mood == ESCAPE_MOOD)
				item->goal_anim_state = MONK_RUN;
			else if (info.ahead && info.distance < MONK_CLOSE_RANGE)
			{
				random = (sint16)GetRandomControl();
				if (random < 0x3000)
					item->goal_anim_state = MONK_ATTACK2;
				else if (random < 0x6000)
					item->goal_anim_state = MONK_AIM3;
				else
					item->goal_anim_state = MONK_WAIT1;
			}
			else if (info.ahead && info.distance < MONK_WALK_RANGE)
				item->goal_anim_state = MONK_WALK;
			else
				item->goal_anim_state = MONK_RUN;
			break;

		case MONK_WALK:
			monk->maximum_turn = MONK_WALK_TURN;

			if (monk->mood == BORED_MOOD)
			{
				if (!monks_attack_lara && info.ahead && lara.target == item)
				{
					if (GetRandomControl() < 0x4000)
						item->goal_anim_state = MONK_WAIT1;
					else
						item->goal_anim_state = MONK_WAIT2;
				}
			}
			else if (monk->mood == ESCAPE_MOOD)
				item->goal_anim_state = MONK_RUN;
			else if (info.ahead && info.distance < MONK_CLOSE_RANGE)
			{
				if (GetRandomControl() < 0x4000)
					item->goal_anim_state = MONK_WAIT1;
				else
					item->goal_anim_state = MONK_WAIT2;
			}
			else if (!info.ahead || info.distance > MONK_WALK_RANGE)
				item->goal_anim_state = MONK_RUN;
			break;

		case MONK_RUN:
			monk->flags &= 0x0fff;
			monk->maximum_turn = MONK_RUN_TURN;
			if (monks_attack_lara)
				monk->maximum_turn += ONE_DEGREE;
			tilt = angle>>2;

			if (monk->mood == BORED_MOOD)
				item->goal_anim_state = MONK_WAIT1;
			else if (monk->mood == ESCAPE_MOOD)
				break;
			else if (info.ahead && info.distance < MONK_CLOSE_RANGE)
			{
				if (GetRandomControl() < 0x4000)
					item->goal_anim_state = MONK_WAIT1;
				else
					item->goal_anim_state = MONK_WAIT2;
			}
			else if (info.ahead && info.distance < MONK_ATTACK5_RANGE)
				item->goal_anim_state = MONK_ATTACK5;
			else if (info.ahead && info.distance < MONK_WALK_RANGE)
			{
				if (GetRandomControl() < 0x4000)
					item->goal_anim_state = MONK_WAIT1;
				else
					item->goal_anim_state = MONK_WAIT2;
			}
			break;

		case MONK_AIM3:
			if (!info.ahead || info.distance > MONK_CLOSE_RANGE)
				item->goal_anim_state = MONK_WAIT2;
			else
				item->goal_anim_state = MONK_ATTACK3;
			break;

		case MONK_ATTACK1:
		case MONK_ATTACK2:
		case MONK_ATTACK3:
		case MONK_ATTACK4:
		case MONK_ATTACK5:
			enemy = monk->enemy;
			if (enemy == lara_item)
			{
				if (!(monk->flags&0xf000) && (item->touch_bits & MONK_TOUCH))
				{
					lara_item->hit_points -= MONK_BIFF_DAMAGE;
					lara_item->hit_status = 1;

					monk->flags |= 0x1000;
					SoundEffect(245, &item->pos, 0);
					CreatureEffect(item, &monk_hit, DoBloodSplat);
				}
			}
			else
			{
				if (!(monk->flags&0xf000) && enemy)
				{
					if (ABS(enemy->pos.x_pos - item->pos.x_pos) < MONK_HIT_RANGE && 
						 ABS(enemy->pos.y_pos - item->pos.y_pos) < MONK_HIT_RANGE &&
						 ABS(enemy->pos.z_pos - item->pos.z_pos) < MONK_HIT_RANGE)
					{
						enemy->hit_points -= MONK_BIFF_ENEMY_DAMAGE;
						enemy->hit_status = 1;

						monk->flags |= 0x1000;
						SoundEffect(245, &item->pos, 0);
					}
				}
			}
			break;
		}
	}

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

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


void Worker3Control(sint16 item_number)
{
	/* Rig worker III */
	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 != WORK3_DEATH)
		{
			item->anim_number = objects[WORKER3].anim_index + WORK3_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = WORK3_DEATH;
		}
	}
	else
	{
		CreatureAIInfo(item, &info);

		CreatureMood(item, &info, VIOLENT);

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

		switch (item->current_anim_state)
		{
		case WORK3_STOP:
			worker->flags = 0;
			if (info.ahead)
				neck = info.angle;

			worker->maximum_turn = 0;

			if (worker->mood == ESCAPE_MOOD)
				item->goal_anim_state = WORK3_RUN;
			else if (worker->mood == BORED_MOOD)
			{
				if (item->required_anim_state)
					item->goal_anim_state = item->required_anim_state;
				else if (GetRandomControl() < 0x4000)
					item->goal_anim_state = WORK3_WALK;
				else
					item->goal_anim_state = WORK3_WAIT;
			}
			else if (info.bite && info.distance < WORK3_ATTACK0_RANGE)
				item->goal_anim_state = WORK3_AIM0;
			else if (info.bite && info.distance < WORK3_ATTACK1_RANGE)
				item->goal_anim_state = WORK3_AIM1;
			else if (info.bite && info.distance < WORK3_WALK_RANGE)
				item->goal_anim_state = WORK3_WALK;
			else
				item->goal_anim_state = WORK3_RUN;
			break;

		case WORK3_WAIT:
			if (info.ahead)
				neck = info.angle;
			
			if (worker->mood != BORED_MOOD)
				item->goal_anim_state = WORK3_STOP;
			else if (GetRandomControl() < WORK3_WALK_CHANCE)
			{
				item->required_anim_state = WORK3_WALK;
				item->goal_anim_state = WORK3_STOP;
			}
			break;

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

			worker->maximum_turn = WORK3_WALK_TURN;

			if (worker->mood == ESCAPE_MOOD)
				item->goal_anim_state = WORK3_RUN;
			else if (worker->mood == BORED_MOOD)
			{
				if (GetRandomControl() < WORK3_WAIT_CHANCE)
				{
					item->required_anim_state = WORK3_WAIT;
					item->goal_anim_state = WORK3_STOP;
				}
			}
			else if (info.bite && info.distance < WORK3_ATTACK0_RANGE)
				item->goal_anim_state = WORK3_STOP;
			else if (info.bite && info.distance < WORK3_ATTACK2_RANGE)
				item->goal_anim_state = WORK3_AIM2;
			else //if (!info.ahead || info.distance > WORK3_WALK_RANGE)
				item->goal_anim_state = WORK3_RUN;
			break;

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

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

			if (worker->mood == ESCAPE_MOOD)
				break;
			else if (worker->mood == BORED_MOOD)
				item->goal_anim_state = WORK3_WALK;
			else if (info.ahead && info.distance < WORK3_WALK_RANGE)
				item->goal_anim_state = WORK3_WALK;
			break;

		case WORK3_AIM0:
			if (info.ahead)
				head = info.angle;
						
			worker->flags = 0;
			if (info.bite && info.distance < WORK3_ATTACK0_RANGE)
				item->goal_anim_state = WORK3_PUNCH0;
			else
				item->goal_anim_state = WORK3_STOP;
			break;

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

			worker->flags = 0;
			if (info.ahead && info.distance < WORK3_ATTACK1_RANGE)
				item->goal_anim_state = WORK3_PUNCH1;
			else
				item->goal_anim_state = WORK3_STOP;
			break;

		case WORK3_AIM2:
			if (info.ahead)
				head = info.angle;
			
			worker->flags = 0;
			if (info.bite && info.distance < WORK3_ATTACK2_RANGE)
				item->goal_anim_state = WORK3_PUNCH2;
			else
				item->goal_anim_state = WORK3_WALK;
			break;

		case WORK3_PUNCH0:
			if (info.ahead)
				head = info.angle;
			
			if (!worker->flags && (item->touch_bits & WORK3_TOUCH))
			{
				lara_item->hit_points -= WORK3_HIT_DAMAGE;
				lara_item->hit_status = 1;
				CreatureEffect(item, &work3_hit, DoBloodSplat);
				SoundEffect(72, &item->pos, 0);

				worker->flags = 1;
			}
			break;

		case WORK3_PUNCH1:
			if (info.ahead)
				head = info.angle;
			
			if (!worker->flags && (item->touch_bits & WORK3_TOUCH))
			{
				lara_item->hit_points -= WORK3_HIT_DAMAGE;
				lara_item->hit_status = 1;
				CreatureEffect(item, &work3_hit, DoBloodSplat);
				SoundEffect(71, &item->pos, 0);

				worker->flags = 1;
			}

			if (info.ahead && info.distance > WORK3_ATTACK1_RANGE && info.distance < WORK3_ATTACK2_RANGE)
				item->goal_anim_state = WORK3_PUNCH2;
			break;

		case WORK3_PUNCH2:
			if (info.ahead)
				head = info.angle;
						
			if (worker->flags!=2 && (item->touch_bits & WORK3_TOUCH))
			{
				lara_item->hit_points -= WORK3_SWIPE_DAMAGE;
				lara_item->hit_status = 1;
				CreatureEffect(item, &work3_hit, DoBloodSplat);
				SoundEffect(71, &item->pos, 0);
				
				worker->flags = 2;
			}
			break;
		}
	}

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

	/* Actually do animation allowing for collisions */
	if (item->current_anim_state < WORK3_CLIMB3) // Know CLIMB3 marks the start of the CLIMB states
	{
		switch (CreatureVault(item_number, angle, 2, WORK3_VAULT_SHIFT))
		{
		case 2:
			/* Half block jump */
			item->anim_number = objects[WORKER3].anim_index + WORK3_CLIMB1_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = WORK3_CLIMB1;
			break;

		case 3:
			/* 3/4 block jump */
			item->anim_number = objects[WORKER3].anim_index + WORK3_CLIMB2_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = WORK3_CLIMB2;
			break;

		case 4:
			/* Full block jump */
			item->anim_number = objects[WORKER3].anim_index + WORK3_CLIMB3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = WORK3_CLIMB3;
			break;
		case -4:
			/* Full block fall */
			item->anim_number = objects[WORKER3].anim_index + WORK3_FALL3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = WORK3_FALL3;
			break;
		}
	}
	else
		CreatureAnimation(item_number, angle, 0);
}


void DrawXianLord(ITEM_INFO *item)
{
	/* Big huge copy of DrawAnimatingItem() which swaps meshes */
	int 		clip,i,poppush;
	OBJECT_INFO *object;
	sint32 		*bone;
	sint16		*rotation1, *rotation2;
	sint16		*extra_rotation;
	sint16		**meshpp, **jadepp;
	uint32 		bit;
	sint16		*frmptr[2];
	int			rate,frac;

	frac=GetFrames( item,frmptr,&rate );

	object = &objects[item->object_number];					// Draw Shadows...
	if (object->shadow_size)
		S_PrintShadow( object->shadow_size,frmptr[0],item,0 );

	phd_PushMatrix();
	phd_TranslateAbs(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	phd_RotYXZ(item->pos.y_rot, item->pos.x_rot, item->pos.z_rot);

	if ( (clip=S_GetObjectBounds(frmptr[0])) )
	{
		CalculateObjectLighting( item,frmptr[0] );

		extra_rotation = (sint16 *)item->data;

		if (item->object_number == CHINESE1)
		{
			meshpp = &meshes[objects[CHINESE1].mesh_index];
			jadepp = &meshes[objects[CHINESE2].mesh_index];
		}
		else
		{
			meshpp = &meshes[objects[CHINESE3].mesh_index];
			jadepp = &meshes[objects[CHINESE4].mesh_index];
		}
		bone = bones + object->bone_index;              	// Bone pointer
		bit = 1;

		if ( !frac )
		{
			phd_TranslateRel((sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8));
			rotation1 = frmptr[0]+9;
			gar_RotYXZsuperpack(&rotation1, 0);

			if (bit & item->mesh_bits)
				phd_PutPolygons( *meshpp, clip );
			else
				phd_PutPolygons( *jadepp, clip );
			meshpp++;
			jadepp++;

			for ( i=object->nmeshes-1; i>0; i--,bone+=4,meshpp++,jadepp++ )
			{
				poppush = *(bone);							// Do Automated Hierarchy
				if (poppush & 1)                			// Push Pop
					phd_PopMatrix();
				if (poppush & 2)
					phd_PushMatrix();

				phd_TranslateRel( *(bone+1), *(bone+2), *(bone+3) );
				gar_RotYXZsuperpack(&rotation1, 0);

				if (extra_rotation && (poppush & (ROT_X|ROT_Y|ROT_Z)))			// If any Extra rotations required...
				{
					if (poppush & ROT_Y)
						phd_RotY(*(extra_rotation++));
					if (poppush & ROT_X)
						phd_RotX(*(extra_rotation++));
					if (poppush & ROT_Z)
						phd_RotZ(*(extra_rotation++));
				}

				bit <<= 1;
				if (bit & item->mesh_bits)
					phd_PutPolygons( *meshpp, clip );
				else
					phd_PutPolygons( *jadepp, clip );
			}
		}
		else
		{
			InitInterpolate( frac,rate );
			phd_TranslateRel_ID( (sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8),
								 (sint32)*(frmptr[1]+6), (sint32)*(frmptr[1]+7), (sint32)*(frmptr[1]+8) );
			rotation1 = frmptr[0]+9;
			rotation2 = frmptr[1]+9;
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);

			if (bit & item->mesh_bits)
				phd_PutPolygons_I( *meshpp, clip );
			else
				phd_PutPolygons_I( *jadepp, clip );
			meshpp++;
			jadepp++;

			for ( i=object->nmeshes-1; i>0; i--,bone+=4,meshpp++,jadepp++ )
			{
				poppush = *(bone);						// Do Automated Hierarchy
				if (poppush & 1)                			// Push Pop
					phd_PopMatrix_I();
				if (poppush & 2)
					phd_PushMatrix_I();

				phd_TranslateRel_I(*(bone+1), *(bone+2), *(bone+3));
				gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);

				if (extra_rotation && (poppush & (ROT_X|ROT_Y|ROT_Z)))			// If any Extra rotations required...
				{
					if (poppush & ROT_Y)
						phd_RotY_I(*(extra_rotation++));
					if (poppush & ROT_X)
						phd_RotX_I(*(extra_rotation++));
					if (poppush & ROT_Z)
						phd_RotZ_I(*(extra_rotation++));
				}

				bit <<= 1;
				if (bit & item->mesh_bits)
					phd_PutPolygons_I( *meshpp, clip );
				else
					phd_PutPolygons_I( *jadepp, clip );
			}

		}
	}
	phd_PopMatrix();
}


void XianDamage(ITEM_INFO *item, CREATURE_INFO *xian, int damage)
{
	if (!(xian->flags&1) && (item->touch_bits & XIAN_TOUCHR))
	{
		lara_item->hit_points -= damage;
		lara_item->hit_status = 1;
		CreatureEffect(item, &xian_right, DoBloodSplat);
		xian->flags |= 1;
		SoundEffect(318, &item->pos, 0);
	}

	if (!(xian->flags&2) && (item->touch_bits & XIAN_TOUCHL))
	{
		lara_item->hit_points -= damage;
		lara_item->hit_status = 1;
		CreatureEffect(item, &xian_left, DoBloodSplat);
		xian->flags |= 2;
		SoundEffect(318, &item->pos, 0);
	}
}


void InitialiseXianLord(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	/* Start XianLord in kneeling down pose, and all his meshes will be jade (from CHINESE2) */
	if (item->object_number == CHINESE1)
	{
		item->anim_number = objects[CHINESE1].anim_index + XIAN_START_ANIM;
		item->frame_number = anims[item->anim_number].frame_base;
		item->current_anim_state = item->goal_anim_state = XIAN_START;
	}
	item->status = NOT_ACTIVE;

	item->mesh_bits = 0;
}


void XianLordControl(sint16 item_number)
{
	/* Chinese 1: man with 2 sticks */
	ITEM_INFO *item;
	CREATURE_INFO *xian;
	sint16 angle, head, neck, tilt;
	int random, lara_alive;
	AI_INFO info;

	if (!CreatureActive(item_number))
		return;

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

	lara_alive = (lara_item->hit_points>0);

	if (item->hit_points <= 0)
	{
		/* Turn him back to jade in whatever position he happens to be in */
		item->current_anim_state = XIAN_DEATH;
		item->mesh_bits >>= 1;

		if (!item->mesh_bits)
		{
			SoundEffect(105, NULL, 0);
			item->mesh_bits = 0xffffffff;
			item->object_number = CHINESE2; // just to fool ExplodingDeath to produce jade chunks
			ExplodingDeath(item_number, 0xffffffff, 0);
			item->object_number = CHINESE1;
			DisableBaddieAI(item_number);
			KillItem(item_number);
			item->status = DEACTIVATED;
			item->flags |= ONESHOT;
		}
		return;
	}
	else
	{
		CreatureAIInfo(item, &info);

		CreatureMood(item, &info, VIOLENT);

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

		if (item->current_anim_state != XIAN_START) // for reload
			item->mesh_bits = 0xffffffff;

		switch (item->current_anim_state)
		{
		case XIAN_START:
			/* Make jade man come to life!! */
			if (!xian->flags)
			{
				item->mesh_bits = (item->mesh_bits<<1) + 1;
				xian->flags = 3;
			}
			else
				xian->flags--;
			break;

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

			xian->maximum_turn = 0;

			if (xian->mood == BORED_MOOD)
			{
				random = GetRandomControl();
				if (random < XIAN_STOP_CHANCE)
					item->goal_anim_state = XIAN_STOP2;
				else if (random < XIAN_WALK_CHANCE)
					item->goal_anim_state = XIAN_WALK;
			}
			else if (info.ahead && info.distance < XIAN_ATTACK1_RANGE)
				item->goal_anim_state = XIAN_AIM1;
			else
				item->goal_anim_state = XIAN_WALK;
			break;

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

			xian->maximum_turn = 0;

			if (xian->mood == ESCAPE_MOOD)
				item->goal_anim_state = XIAN_WALK;
			else if (xian->mood == BORED_MOOD)
			{
				random = GetRandomControl();
				if (random < XIAN_STOP_CHANCE)
					item->goal_anim_state = XIAN_STOP;
				else if (random < XIAN_WALK_CHANCE)
					item->goal_anim_state = XIAN_WALK;
			}
			else if (info.ahead && info.distance < XIAN_ATTACK5_RANGE)
				item->goal_anim_state = XIAN_AIM5;
			else
				item->goal_anim_state = XIAN_WALK;
			break;

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

			xian->maximum_turn = XIAN_WALK_TURN;

			if (xian->mood == ESCAPE_MOOD)
				item->goal_anim_state = XIAN_RUN;
			else if (xian->mood == BORED_MOOD)
			{
				random = GetRandomControl();
				if (random < XIAN_STOP_CHANCE)
					item->goal_anim_state = XIAN_STOP;
				else if (random < XIAN_WALK_CHANCE)
					item->goal_anim_state = XIAN_STOP2;
			}
			else if (info.ahead && info.distance < XIAN_ATTACK4_RANGE)
			{
				if (info.distance < XIAN_ATTACK2_RANGE)
					item->goal_anim_state = XIAN_AIM2;
				else if (GetRandomControl() < 0x4000)
					item->goal_anim_state = XIAN_AIM3;
				else
					item->goal_anim_state = XIAN_AIM4;
			}
			else if (!info.ahead || info.distance > XIAN_RUN_RANGE)
				item->goal_anim_state = XIAN_RUN;
			break;

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

			xian->maximum_turn = XIAN_RUN_TURN;

			if (xian->mood == ESCAPE_MOOD)
				break;
			else if (xian->mood == BORED_MOOD)
			{
				if (GetRandomControl() < 0x4000)
					item->goal_anim_state = XIAN_STOP;
				else
					item->goal_anim_state = XIAN_STOP2;
			}
			else if (info.ahead && info.distance < XIAN_ATTACK6_RANGE) // the only way he'll ever break out of a run is to attack Lara
				item->goal_anim_state = XIAN_AIM6;
			break;

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

			xian->flags = 0;
			if (!info.ahead || info.distance > XIAN_ATTACK1_RANGE)
				item->goal_anim_state = XIAN_STOP;
			else
				item->goal_anim_state = XIAN_HIT1;
			break;

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

			xian->flags = 0;
			if (!info.ahead || info.distance > XIAN_ATTACK2_RANGE)
				item->goal_anim_state = XIAN_WALK;
			else
				item->goal_anim_state = XIAN_HIT2;
			break;

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

			xian->flags = 0;
			if (!info.ahead || info.distance > XIAN_ATTACK3_RANGE)
				item->goal_anim_state = XIAN_WALK;
			else
				item->goal_anim_state = XIAN_HIT2;
			break;

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

			xian->flags = 0;
			if (!info.ahead || info.distance > XIAN_ATTACK4_RANGE)
				item->goal_anim_state = XIAN_WALK;
			else
				item->goal_anim_state = XIAN_HIT2;
			break;

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

			xian->flags = 0;
			if (!info.ahead || info.distance > XIAN_ATTACK5_RANGE)
				item->goal_anim_state = XIAN_STOP2;
			else
				item->goal_anim_state = XIAN_HIT5;
			break;

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

			xian->flags = 0;
			if (!info.ahead || info.distance > XIAN_ATTACK6_RANGE)
				item->goal_anim_state = XIAN_RUN;
			else
				item->goal_anim_state = XIAN_HIT6;
			break;

		case XIAN_HIT1:
			// the standing slap
			XianDamage(item, xian, XIAN_HIT1_DAMAGE);
			break;

		case XIAN_HIT2:
		case XIAN_HIT3:
		case XIAN_HIT4:
			if (info.ahead)
				head = info.angle;

			// the walking swings
			XianDamage(item, xian, XIAN_HIT2_DAMAGE);

			if (info.ahead && info.distance < XIAN_ATTACK1_RANGE)
			{
				if (GetRandomControl() < 0x4000)
					item->goal_anim_state = XIAN_STOP;
				else
					item->goal_anim_state = XIAN_STOP2;
			}
			else
				item->goal_anim_state = XIAN_WALK;
			break;

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

			// the STOP2 prod which can go straight into the STOP smack
			XianDamage(item, xian, XIAN_HIT5_DAMAGE);

			if (info.ahead && info.distance < XIAN_ATTACK1_RANGE)
				item->goal_anim_state = XIAN_STOP;
			else
				item->goal_anim_state = XIAN_STOP2;
			break;

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

			// running attack which can go to almost any state (cheers Joss)
			XianDamage(item, xian, XIAN_HIT6_DAMAGE);

			if (info.ahead && info.distance < XIAN_ATTACK1_RANGE)
			{
				if (GetRandomControl() < 0x4000)
					item->goal_anim_state = XIAN_STOP;
				else 
					item->goal_anim_state = XIAN_STOP2;
			}
			else if (info.ahead && info.distance < XIAN_ATTACK4_RANGE)
				item->goal_anim_state = XIAN_WALK;
			else
				item->goal_anim_state = XIAN_RUN;
			break;
		}
	}

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

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

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


void WarriorSparkleTrail(ITEM_INFO *item)
{
	FX_INFO *fx;
	int fx_number;

	fx_number = CreateEffect(item->room_number);
	if (fx_number != NO_ITEM)
	{
		fx = &effects[fx_number];
		fx->object_number = TWINKLE;
		fx->pos.x_pos = item->pos.x_pos + (GetRandomDraw() * 256 >> 15) - 128;
		fx->pos.y_pos = item->pos.y_pos + (GetRandomDraw() * 256 >> 15) - 256;
		fx->pos.z_pos = item->pos.z_pos + (GetRandomDraw() * 256 >> 15) - 128;
		fx->room_number = item->room_number;
		fx->counter = -30;
		fx->frame_number = 0;
	}

	SoundEffect(312, &item->pos, 0);
}

void WarriorControl(sint16 item_number)
{
	ITEM_INFO *item;
	CREATURE_INFO *warrior;
	sint16 angle, head, neck, tilt;
	AI_INFO info;

	if (!CreatureActive(item_number))
		return;

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

	if (item->hit_points <= 0)
	{
		/* Turn him back to jade in whatever position he happens to be in */
		item->current_anim_state = WARRIOR_DEATH;
		item->mesh_bits >>= 1;

		if (!item->mesh_bits)
		{
			SoundEffect(105, NULL, 0);
			item->mesh_bits = 0xffffffff;
			item->object_number = CHINESE4; // just to fool ExplodingDeath to produce jade chunks
			ExplodingDeath(item_number, 0xffffffff, 0);
			item->object_number = CHINESE3;
			DisableBaddieAI(item_number);
			KillItem(item_number);
			item->status = DEACTIVATED;
			item->flags |= ONESHOT;
		}
		return;
	}
	else
	{
		/* Get ground based information */
		warrior->LOT.step = STEP_L;
		warrior->LOT.drop = -STEP_L;
		warrior->LOT.fly = NO_FLYING;
		CreatureAIInfo(item, &info);

		if (item->current_anim_state == WARRIOR_FLY)
		{
			/* If flying and not in same zone, then use fly zone */
			if (info.zone_number != info.enemy_zone)
			{
				warrior->LOT.step = WALL_L*20;
				warrior->LOT.drop = -WALL_L*20;
				warrior->LOT.fly = STEP_L/4;
				CreatureAIInfo(item, &info);
			}
		}

		CreatureMood(item, &info, VIOLENT);

		angle = CreatureTurn(item, warrior->maximum_turn);
		
		if (item->current_anim_state != WARRIOR_START) // for reload
			item->mesh_bits = 0xffffffff;

		switch (item->current_anim_state)
		{
		case WARRIOR_START:
			/* Make jade man come to life!! */
			if (!warrior->flags)
			{
				item->mesh_bits = (item->mesh_bits<<1) + 1;
				warrior->flags = 3;
			}
			else
				warrior->flags--;
			break;

		case WARRIOR_STOP:
			warrior->maximum_turn = 0;
			if (info.ahead)
				neck = info.angle;

			if (lara_item->hit_points <= 0)
				item->goal_anim_state = WARRIOR_WAIT;
			else if (info.bite && info.distance < WARRIOR_ATTACK1_RANGE)
			{
				if (GetRandomControl() < 0x4000)
					item->goal_anim_state = WARRIOR_AIM1;
				else
					item->goal_anim_state = WARRIOR_AIM2;
			}
			else if (info.zone_number != info.enemy_zone)
				item->goal_anim_state = WARRIOR_FLY;
			else
				item->goal_anim_state = WARRIOR_WALK;
			break;

		case WARRIOR_WALK:
			warrior->maximum_turn = WARRIOR_WALK_TURN;
			if (info.ahead)
				neck = info.angle;

			if (lara_item->hit_points <= 0)
				item->goal_anim_state = WARRIOR_STOP;
			else if (info.bite && info.distance < WARRIOR_ATTACK3_RANGE)
				item->goal_anim_state = WARRIOR_AIM3;
			else if (info.zone_number != info.enemy_zone)
				item->goal_anim_state = WARRIOR_STOP;
			break;

		case WARRIOR_FLY:
			warrior->maximum_turn = WARRIOR_FLY_TURN;
			if (info.ahead)
				neck = info.angle;

			WarriorSparkleTrail(item);

			if (warrior->LOT.fly == NO_FLYING)
				item->goal_anim_state = WARRIOR_STOP;
			break;

		case WARRIOR_AIM1:
			warrior->flags = 0;
			if (info.ahead)
				head = info.angle;

			if (info.bite && info.distance < WARRIOR_ATTACK1_RANGE)
				item->goal_anim_state = WARRIOR_SLASH1;
			else
				item->goal_anim_state = WARRIOR_STOP;
			break;

		case WARRIOR_AIM2:
			warrior->flags = 0;
			if (info.ahead)
				head = info.angle;

			if (info.bite && info.distance < WARRIOR_ATTACK1_RANGE)
				item->goal_anim_state = WARRIOR_SLASH2;
			else
				item->goal_anim_state = WARRIOR_STOP;
			break;

		case WARRIOR_AIM3:
			warrior->flags = 0;
			if (info.ahead)
				head = info.angle;

			if (info.bite && info.distance < WARRIOR_ATTACK3_RANGE)
				item->goal_anim_state = WARRIOR_SLASH3;
			else
				item->goal_anim_state = WARRIOR_WALK;
			break;

		case WARRIOR_SLASH1:
		case WARRIOR_SLASH2:
		case WARRIOR_SLASH3:
			if (info.ahead)
				head = info.angle;

			if (!warrior->flags && (item->touch_bits & WARRIOR_TOUCH))
			{
				lara_item->hit_status = 1;
				lara_item->hit_points -= WARRIOR_HACK_DAMAGE;

				CreatureEffect(item, &warrior_sword, DoBloodSplat);

				warrior->flags = 1;
			}
			break;
		}
	}

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

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