/*********************************************************************************************/
/*                                                                                           */
/* Area 51 Prisoner - "Bob"                                                                        */
/*                                                                                           */
/*********************************************************************************************/

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

#include "objects.h"
#include "lara.h"
#include "sound.h"
#include "control.h"

void InitialisePrisoner(sint16 item_number);
void PrisonerControl(sint16 item_number);

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

void *func[] = {
	&InitialisePrisoner,
	&PrisonerControl,
};
#endif

extern CREATURE_INFO *baddie_slots;

//#define BOB_HIT_DAMAGE 80
//#define BOB_SWIPE_DAMAGE 100
#define BOB_HIT_DAMAGE 40
#define BOB_SWIPE_DAMAGE 50


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

// Area 51 Prisoner: Why is he called Bob?

BITE_INFO bob_hit = {10,10,11, 13};

enum bob_anims {BOB_EMPTY, BOB_STOP, BOB_WALK, BOB_PUNCH2, BOB_AIM2, BOB_WAIT, BOB_AIM1, BOB_AIM0, BOB_PUNCH1, BOB_PUNCH0,
	BOB_RUN, BOB_DEATH, BOB_CLIMB3, BOB_CLIMB1, BOB_CLIMB2, BOB_FALL3};

#define BOB_WALK_TURN (ONE_DEGREE*7)
#define BOB_RUN_TURN (ONE_DEGREE*11)

#define BOB_ATTACK0_RANGE SQUARE(WALL_L/3)
#define BOB_ATTACK1_RANGE SQUARE(WALL_L*2/3)
#define BOB_ATTACK2_RANGE SQUARE(WALL_L*3/4)

#define BOB_WALK_RANGE SQUARE(WALL_L)

#define BOB_WALK_CHANCE 0x100
#define BOB_WAIT_CHANCE 0x100

#define BOB_DIE_ANIM 26
#define BOB_STOP_ANIM 6

#define BOB_CLIMB1_ANIM 28
#define BOB_CLIMB2_ANIM 29
#define BOB_CLIMB3_ANIM 27
#define BOB_FALL3_ANIM  30

#define BOB_TOUCH 0x2400

#define BOB_VAULT_SHIFT 260
#define BOB_AWARE_DISTANCE SQUARE(WALL_L)
#define BOB_HIT_RADIUS (STEP_L)

//#define DEBUG_BOB

#ifdef DEBUG_BOB
extern char exit_message[];
#endif

#ifdef DEBUG_BOB
static char *BobStrings[] = {"EMPTY", "STOP", "WALK", "PUNCH2", "AIM2", "WAIT", "AIM1", "AIM0", "PUNCH1", "PUNCH0",
	"RUN", "DEATH", "CLIMB3", "CLIMB1", "CLIMB2", "FALL3"};
#endif

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

void InitialisePrisoner(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];
	InitialiseCreature(item_number);

	/* Start Bob in stop pose*/
	item->anim_number = objects[BOB].anim_index + BOB_STOP_ANIM;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = item->goal_anim_state = BOB_STOP;
}

void PrisonerControl(sint16 item_number)
{
	// Area 51 Prisoner "Bob"
	ITEM_INFO *item, *enemy, *target;
	CREATURE_INFO *bob, *cinfo;
	sint16 angle, torso_y, torso_x, head, tilt, slot;
	sint32 lara_dx, lara_dz, x, z, distance, best_distance;
	AI_INFO info, lara_info;

	if (!CreatureActive(item_number))
		return;

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

	#ifdef DEBUG_BOB
//	sprintf(exit_message, "Mood: %d", bob->mood);
//	PrintDbug(2, 2, exit_message);
	sprintf(exit_message, "%s", BobStrings[item->current_anim_state]);
	PrintDbug(2, 4, exit_message);
	sprintf(exit_message, "%s", BobStrings[item->goal_anim_state]);
	PrintDbug(2, 5, exit_message);
//	sprintf(exit_message, "%s", BobStrings[item->required_anim_state]);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Anim Number; %d", item->anim_number - objects[BOB].anim_index);
//	PrintDbug(2, 6, exit_message);
	sprintf(exit_message, "Alert:%d, Goal:%d, Hurt:%d", bob->alerted, bob->reached_goal, bob->hurt_by_lara);
	PrintDbug(2,6, exit_message);

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

	if (boxes[item->box_number].overlap_index & BLOCKED)
	{
		DoLotsOfBloodD(item->pos.x_pos, item->pos.y_pos-(GetRandomControl()&255)-32, item->pos.z_pos, (GetRandomControl()&127)+128, GetRandomControl()<<1, item->room_number , 3);
		item->hit_points -= 20;
	}

	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != BOB_DEATH)
		{
			item->anim_number = objects[BOB].anim_index + BOB_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BOB_DEATH;
			bob->LOT.step = STEP_L;
		}
	}
	else
	{
		if (item->ai_bits && item->ai_bits != MODIFY)
			GetAITarget(bob);
		else if (bob->hurt_by_lara)
			bob->enemy = lara_item;
		else
		{
			bob->enemy = NULL;
			best_distance = 0x7fffffff;
			cinfo = baddie_slots;
			for (slot=0; slot<NUM_SLOTS; slot++, cinfo++)
			{
				if (cinfo->item_num==NO_ITEM || cinfo->item_num==item_number)
					continue;
				target = &items[cinfo->item_num];
				if (target->object_number == LARA  || target->object_number == BOB || target->object_number == ROBOT_SENTRY_GUN || target->hit_points <= 0)
					continue;
				/* Is this target closest? */
				x = target->pos.x_pos - item->pos.x_pos;
				z = target->pos.z_pos - item->pos.z_pos;
				if (z>32000 || z<-32000 || x>32000 || x<-32000)	// Well out of range.
					continue;
				distance = x*x + z*z;
				if (distance < best_distance)
				{
					bob->enemy = target;
					best_distance = distance;
				}
			}

		}

		if (item->ai_bits == MODIFY)
			item->hit_points = 200; //Indestructible Bob!

		CreatureAIInfo(item, &info);

		if (!bob->hurt_by_lara && bob->enemy == lara_item)
			bob->enemy = NULL;

		if (bob->enemy == lara_item)
		{
			lara_info.angle = info.angle;
			lara_info.distance = info.distance;
		}
		else
		{
			lara_dz = lara_item->pos.z_pos - item->pos.z_pos;
			lara_dx = lara_item->pos.x_pos - item->pos.x_pos;
			lara_info.angle = phd_atan(lara_dz, lara_dx) - item->pos.y_rot; //only need to fill out the bits of lara_info that will be needed by TargetVisible
			lara_info.distance = lara_dz * lara_dz + lara_dx * lara_dx;
		}

		GetCreatureMood(item, &info, VIOLENT);

		CreatureMood(item, &info, VIOLENT);


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

#ifdef DEBUG_BOB
		sprintf(exit_message, "head: %d, Target Visible?: %d", bob->joint_rotation[1], TargetVisible(item, &info));
		PrintDbug(2, 5, exit_message);
#endif

/*
		enemy = bob->enemy; //TargetVisible uses enemy, so need to fill this in as lara if we're doing other things
		bob->enemy = lara_item;
		if ((lara_info.distance < BOB_AWARE_DISTANCE || item->hit_status || TargetVisible(item, &lara_info)) && !(item->ai_bits & FOLLOW)) //Maybe move this into LONDSEC_WAIT case?
			AlertAllGuards(item_number);
		bob->enemy = enemy;
*/
		if (bob->hurt_by_lara)
		{
			if (!bob->alerted)
				SoundEffect(300, &item->pos, 0);
			AlertAllGuards(item_number);
		}

		enemy = bob->enemy;

		switch (item->current_anim_state)
		{
			case BOB_WAIT:
				if (bob->alerted || item->goal_anim_state == BOB_RUN)
				{
					item->goal_anim_state = BOB_STOP;
					break;
				}

			case BOB_STOP:
				bob->flags = 0;

				bob->maximum_turn = 0;

				head = lara_info.angle;

				if (item->ai_bits & GUARD)
				{
					head = AIGuard(bob);
					if (!(GetRandomControl() & 0xFF))
					{
						if (item->current_anim_state == BOB_STOP)
							item->goal_anim_state = BOB_WAIT;
						else
							item->goal_anim_state = BOB_STOP;
					}
					break;
				}

				else if (item->ai_bits & PATROL1)
					item->goal_anim_state = BOB_WALK;

				else if (bob->mood == ESCAPE_MOOD)
				{
					if (lara.target != item && info.ahead && !item->hit_status)
						item->goal_anim_state = BOB_STOP;
					else
						item->goal_anim_state = BOB_RUN;
				}
				else if (bob->mood == BORED_MOOD || ((item->ai_bits & FOLLOW ) && (bob->reached_goal || lara_info.distance > SQUARE(WALL_L*2))))
				{
					if (item->required_anim_state)
						item->goal_anim_state = item->required_anim_state;
					else if (info.ahead)
						item->goal_anim_state = BOB_STOP;
					else
						item->goal_anim_state = BOB_RUN;
				}
				else if (info.bite && info.distance < BOB_ATTACK0_RANGE)
					item->goal_anim_state = BOB_AIM0;
				else if (info.bite && info.distance < BOB_ATTACK1_RANGE)
					item->goal_anim_state = BOB_AIM1;
				else if (info.bite && info.distance < BOB_WALK_RANGE)
					item->goal_anim_state = BOB_WALK;
				else
					item->goal_anim_state = BOB_RUN;
				break;
			/* - Moved to above BOB-STOP
			case BOB_WAIT:
				if (info.ahead)
					head = info.angle;
				bob->maximum_turn = 0;

				if (bob->mood != BORED_MOOD)
					item->goal_anim_state = BOB_STOP;
				else if (GetRandomControl() < BOB_WALK_CHANCE)
				{
					item->required_anim_state = BOB_WALK;
					item->goal_anim_state = BOB_STOP;
				}
				break;
			*/
			case BOB_WALK:
				head=lara_info.angle;

				bob->maximum_turn = BOB_WALK_TURN;

				if (item->ai_bits & PATROL1)
				{
					item->goal_anim_state = BOB_WALK;
					head=0;
				}
				else if (bob->mood == ESCAPE_MOOD)
					item->goal_anim_state = BOB_RUN;
				else if (bob->mood == BORED_MOOD)
				{
					if (GetRandomControl() < BOB_WAIT_CHANCE)
					{
						item->required_anim_state = BOB_WAIT;
						item->goal_anim_state = BOB_STOP;
					}
				}
				else if (info.bite && info.distance < BOB_ATTACK0_RANGE)
					item->goal_anim_state = BOB_STOP;
				else if (info.bite && info.distance < BOB_ATTACK2_RANGE)
					item->goal_anim_state = BOB_AIM2;
				else //if (!info.ahead || info.distance > BOB_WALK_RANGE)
					item->goal_anim_state = BOB_RUN;
				break;

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

				bob->maximum_turn = BOB_RUN_TURN;
				tilt = angle/2;

				if (item->ai_bits & GUARD)
					item->goal_anim_state = BOB_STOP;
				else if (bob->mood == ESCAPE_MOOD)
				{
					if (lara.target != item && info.ahead)
						item->goal_anim_state = BOB_STOP;
					break;
				}
				else if ((item->ai_bits & FOLLOW ) && (bob->reached_goal || lara_info.distance > SQUARE(WALL_L*2)))
					item->goal_anim_state = BOB_STOP;	//Maybe BOB_WAIT?
				else if (bob->mood == BORED_MOOD)
					item->goal_anim_state = BOB_WALK;
				else if (info.ahead && info.distance < BOB_WALK_RANGE)
					item->goal_anim_state = BOB_WALK;
				break;

			case BOB_AIM0:
				if (info.ahead)
				{
					torso_y = info.angle;
					torso_x = info.x_angle;
				}
				bob->maximum_turn = BOB_WALK_TURN;

				bob->flags = 0;
				if (info.bite && info.distance < BOB_ATTACK0_RANGE)
					item->goal_anim_state = BOB_PUNCH0;
				else
					item->goal_anim_state = BOB_STOP;
				break;

			case BOB_AIM1:
				if (info.ahead)
				{
					torso_y = info.angle;
					torso_x = info.x_angle;
				}
				bob->maximum_turn = BOB_WALK_TURN;

				bob->flags = 0;
				if (info.ahead && info.distance < BOB_ATTACK1_RANGE)
					item->goal_anim_state = BOB_PUNCH1;
				else
					item->goal_anim_state = BOB_STOP;
				break;

			case BOB_AIM2:
				if (info.ahead)
				{
					torso_y = info.angle;
					torso_x = info.x_angle;
				}
				bob->maximum_turn = BOB_WALK_TURN;

				bob->flags = 0;
				if (info.bite && info.distance < BOB_ATTACK2_RANGE)
					item->goal_anim_state = BOB_PUNCH2;
				else
					item->goal_anim_state = BOB_WALK;
				break;

			case BOB_PUNCH0:
				if (info.ahead)
				{
					torso_y = info.angle;
					torso_x = info.x_angle;
				}
				bob->maximum_turn = BOB_WALK_TURN;

				if (enemy == lara_item)
				{
					if (!bob->flags && (item->touch_bits & BOB_TOUCH))
					{
						lara_item->hit_points -= BOB_HIT_DAMAGE;
						lara_item->hit_status = 1;
						CreatureEffect(item, &bob_hit, DoBloodSplat);
						SoundEffect(70, &item->pos, 0);
						bob->flags = 1;
					}
				}
				else
				{
					if (!bob->flags && enemy)
					{
						if (ABS(enemy->pos.x_pos - item->pos.x_pos) < BOB_HIT_RADIUS &&
						 	ABS(enemy->pos.y_pos - item->pos.y_pos) <= BOB_HIT_RADIUS &&
						 	ABS(enemy->pos.z_pos - item->pos.z_pos) < BOB_HIT_RADIUS)
						{
							enemy->hit_points -= BOB_HIT_DAMAGE>>1;
							enemy->hit_status = 1;
							SoundEffect(70, &item->pos, 0);
							bob->flags = 1;
							CreatureEffect(item, &bob_hit, DoBloodSplat);
						}
					}
				}


				break;

			case BOB_PUNCH1:
				if (info.ahead)
				{
					torso_y = info.angle;
					torso_x = info.x_angle;
				}
				bob->maximum_turn = BOB_WALK_TURN;

				if (enemy == lara_item)
				{
					if (!bob->flags && (item->touch_bits & BOB_TOUCH))
					{
						lara_item->hit_points -= BOB_HIT_DAMAGE;
						lara_item->hit_status = 1;
						CreatureEffect(item, &bob_hit, DoBloodSplat);
						SoundEffect(70, &item->pos, 0);
						bob->flags = 1;
					}
				}
				else
				{
					if (!bob->flags && enemy)
					{
						if (ABS(enemy->pos.x_pos - item->pos.x_pos) < BOB_HIT_RADIUS &&
						 	ABS(enemy->pos.y_pos - item->pos.y_pos) <= BOB_HIT_RADIUS &&
						 	ABS(enemy->pos.z_pos - item->pos.z_pos) < BOB_HIT_RADIUS)
						{
							enemy->hit_points -= BOB_HIT_DAMAGE>>1;
							enemy->hit_status = 1;
							bob->flags = 1;
							CreatureEffect(item, &bob_hit, DoBloodSplat);
							SoundEffect(70, &item->pos, 0);
						}
					}
				}


				if (info.ahead && info.distance > BOB_ATTACK1_RANGE && info.distance < BOB_ATTACK2_RANGE)
					item->goal_anim_state = BOB_PUNCH2;
				break;

			case BOB_PUNCH2:
				if (info.ahead)
				{
					torso_y = info.angle;
					torso_x = info.x_angle;
				}
				bob->maximum_turn = BOB_WALK_TURN;

				if (enemy == lara_item)
				{
					if (bob->flags!=2 && (item->touch_bits & BOB_TOUCH))
					{
						lara_item->hit_points -= BOB_SWIPE_DAMAGE;
						lara_item->hit_status = 1;
						CreatureEffect(item, &bob_hit, DoBloodSplat);
						SoundEffect(70, &item->pos, 0);
						bob->flags = 2;
					}
				}
				else
				{
					if (bob->flags!=2 && enemy)
					{
						if (ABS(enemy->pos.x_pos - item->pos.x_pos) < BOB_HIT_RADIUS &&
						 	ABS(enemy->pos.y_pos - item->pos.y_pos) <= BOB_HIT_RADIUS &&
						 	ABS(enemy->pos.z_pos - item->pos.z_pos) < BOB_HIT_RADIUS)
						{
							enemy->hit_points -= BOB_SWIPE_DAMAGE>>1;
							enemy->hit_status = 1;
							SoundEffect(70, &item->pos, 0);
							bob->flags = 2;
							CreatureEffect(item, &bob_hit, DoBloodSplat);
						}
					}
				}


				break;
		}
	}

	CreatureTilt(item, tilt);
	CreatureJoint(item, 0, torso_y);
	CreatureJoint(item, 1, torso_x);
	CreatureJoint(item, 2, head);

#ifdef DEBUG_BOB
	sprintf(exit_message, "AI:%d, alert:%d, target:%d", item->ai_bits, bob->alerted, (bob->enemy == NULL ? -1 : bob->enemy->object_number));
	PrintDbug(2, 2, exit_message);
	sprintf(exit_message, "Mood: %d", bob->mood);
	PrintDbug(2, 3, exit_message);
#endif


	/* Actually do animation allowing for collisions */
//## BUG: was still doing this when he was dying!
//	if (item->current_anim_state < BOB_CLIMB3) // Know CLIMB3 marks the start of the CLIMB states
	if (item->current_anim_state < BOB_DEATH) // Know CLIMB3 marks the start of the CLIMB states
	{
		switch (CreatureVault(item_number, angle, 2, BOB_VAULT_SHIFT))
		{
		case 2:
			/* Half block jump */
			bob->maximum_turn = 0;
			item->anim_number = objects[BOB].anim_index + BOB_CLIMB1_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BOB_CLIMB1;
			break;

		case 3:
			/* 3/4 block jump */
			bob->maximum_turn = 0;
			item->anim_number = objects[BOB].anim_index + BOB_CLIMB2_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BOB_CLIMB2;
			break;

		case 4:
			/* Full block jump */
			bob->maximum_turn = 0;
			item->anim_number = objects[BOB].anim_index + BOB_CLIMB3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BOB_CLIMB3;
			break;
		case -4:
			/* Full block fall */
			bob->maximum_turn = 0;
			item->anim_number = objects[BOB].anim_index + BOB_FALL3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BOB_FALL3;
			break;
		}
	}
	else
	{
		bob->maximum_turn = 0;
		CreatureAnimation(item_number, angle, 0);
	}
}


