/*********************************************************************************************/
/*                                                                                           */
/* Area 51 Baton Man                                                                         */
/*                                                                                           */
/*********************************************************************************************/

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

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

void InitialiseBaton(sint16 item_number);
void BatonControl(sint16 item_number);

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

void *func[] = {
	&InitialiseBaton,
	&BatonControl,
};

#endif

extern CREATURE_INFO	*baddie_slots;

//#define BATON_HIT_DAMAGE 80
//#define BATON_SWIPE_DAMAGE 100
#define BATON_HIT_DAMAGE 80
#define BATON_SWIPE_DAMAGE 100
#define BATON_KICK_DAMAGE 150

#define BATON_HIT_RADIUS (STEP_L)

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

// Area 51 Baton Man: The man with the stick
BITE_INFO baton_hit = {247,10,11, 13};
BITE_INFO baton_kick = {0,0,100,6};

enum baton_anims {BATON_EMPTY, BATON_STOP, BATON_WALK, BATON_PUNCH2, BATON_AIM2, BATON_WAIT, BATON_AIM1, BATON_AIM0, BATON_PUNCH1, BATON_PUNCH0,
	BATON_RUN, BATON_DEATH, BATON_KICK, BATON_CLIMB3, BATON_CLIMB1, BATON_CLIMB2, BATON_FALL3};

#define BATON_WALK_TURN (ONE_DEGREE*6)
#define BATON_RUN_TURN (ONE_DEGREE*7)

#define BATON_ATTACK0_RANGE SQUARE(WALL_L/2)
#define BATON_ATTACK1_RANGE SQUARE(WALL_L)
#define BATON_ATTACK2_RANGE SQUARE(WALL_L*5/4)
#define BATON_KICK_RANGE SQUARE(WALL_L*3/2)
#define BATON_WALK_RANGE SQUARE(WALL_L)

#define BATON_WALK_CHANCE 0x100
#define BATON_WAIT_CHANCE 0x100

#define BATON_DIE_ANIM 26
#define BATON_STOP_ANIM 6

#define BATON_CLIMB1_ANIM 28
#define BATON_CLIMB2_ANIM 29
#define BATON_CLIMB3_ANIM 27
#define BATON_FALL3_ANIM  30

#define BATON_TOUCH 0x2400
#define BATON_KICK_TOUCH 0x60

#define BATON_VAULT_SHIFT 260
#define BATON_AWARE_DISTANCE SQUARE(WALL_L)


//#define DEBUG_BATON

#ifdef DEBUG_BATON
extern char exit_message[];
#endif

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

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

void InitialiseBaton(sint16 item_number)
{
	ITEM_INFO *item;

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

	/* Start Baton in stop pose*/
	item->anim_number = objects[MP1].anim_index + BATON_STOP_ANIM;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = item->goal_anim_state = BATON_STOP;
}

void BatonControl(sint16 item_number)
{
	// Area 51 - Baton Man
	ITEM_INFO *item, *enemy, *target;
	CREATURE_INFO *baton, *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];
	baton = (CREATURE_INFO *)item->data;
	torso_y = torso_x = head = angle = tilt = 0;

	#ifdef DEBUG_BATON
//	sprintf(exit_message, "Mood:%d, LSpeed:%d, fired:%d, Flare:%d", baton->mood, lara_item->speed, lara.has_fired, lara.gun_type == LG_FLARE);
//	PrintDbug(2, 2, exit_message);
//	printf("Mood:%d, AI:%d, Sno:%d, target:%d\n", baton->mood, item->ai_bits, baton->LOT.search_number, baton->enemy == NULL ? -1 : baton->enemy->object_number);
	sprintf(exit_message, "Mood:%d, AI:%d, patrol:%d, target:%d", baton->mood, item->ai_bits, baton->patrol2, baton->enemy == NULL ? -1 : baton->enemy->object_number);
	PrintDbug(2, 2, exit_message);
	sprintf(exit_message, "%s", BatonStrings[item->current_anim_state]);
	PrintDbug(2, 3, exit_message);
	sprintf(exit_message, "%s", BatonStrings[item->goal_anim_state]);
	PrintDbug(2, 4, exit_message);
//	sprintf(exit_message, "%s", BatonStrings[item->required_anim_state]);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Anim Number; %d", item->anim_number - objects[MP1].anim_index);
//	PrintDbug(2, 6, exit_message);
	sprintf(exit_message, "Alert:%d, Goal:%d, Hurt:%d", baton->alerted, baton->reached_goal, baton->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 != BATON_DEATH)
		{
			item->anim_number = objects[MP1].anim_index + BATON_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BATON_DEATH;
			baton->LOT.step = STEP_L;
		}
	}
	else
	{
		if (item->ai_bits)
			GetAITarget(baton);
		else
		{
			baton->enemy = lara_item;
			lara_dz = lara_item->pos.z_pos - item->pos.z_pos;
			lara_dx = lara_item->pos.x_pos - item->pos.x_pos;

			lara_info.distance = lara_dz * lara_dz + lara_dx * lara_dx;
			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 )
					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 && distance < lara_info.distance)
				{
					baton->enemy = target;
					best_distance = distance;
				}
			}

		}

		CreatureAIInfo(item, &info);

		if (baton->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, baton->maximum_turn);


		enemy = baton->enemy; //TargetVisible uses enemy, so need to fill this in as lara if we're doing other things
		baton->enemy = lara_item;
//		if (((lara_info.distance < BATON_AWARE_DISTANCE && abs(lara_item->pos.y_pos - item->pos.y_pos) < WALL_L)|| item->hit_status || TargetVisible(item, &lara_info)) && !(item->ai_bits & FOLLOW)) //Maybe move this into LONDSEC_WAIT case?
//		if ((lara_info.distance < BATON_AWARE_DISTANCE || item->hit_status || TargetVisible(item, &lara_info)) && !(item->ai_bits & FOLLOW))
		if (item->hit_status || ((lara_info.distance < BATON_AWARE_DISTANCE ||  TargetVisible(item, &lara_info)) && (abs(lara_item->pos.y_pos - item->pos.y_pos) < WALL_L))) // TS- TODO: take this back out after demo!!
		{
			if (!baton->alerted)
				SoundEffect(300, &item->pos, 0); 
			AlertAllGuards(item_number);
		}
		baton->enemy = enemy;

	#ifdef DEBUG_BATON
//	sprintf(exit_message, "head: %d, Target Visible?: %d", baton->joint_rotation[1], TargetVisible(item, &info));
//	PrintDbug(2, 5, exit_message);
/*
		x = (enemy->pos.x_pos - item->pos.x_pos) >> 6;
		y = (enemy->pos.y_pos - item->pos.y_pos) >> 6;
		z = (enemy->pos.z_pos - item->pos.z_pos) >> 6;
		distance = x*x + y*y + z*z;

		sprintf(exit_message, "distance: %d", distance);
		PrintDbug(2, 5, exit_message);
*/		
	#endif

		switch (item->current_anim_state)
		{
		case BATON_WAIT:
			if (baton->alerted || item->goal_anim_state == BATON_RUN)
			{
				item->goal_anim_state = BATON_STOP;
				break;
			}

		case BATON_STOP:
			baton->flags = 0;

			baton->maximum_turn = 0;

			head = lara_info.angle;

			if (item->ai_bits & GUARD)
			{
				head = AIGuard(baton);
				if (!(GetRandomControl() & 0xFF))
				{
					if (item->current_anim_state == BATON_STOP)
						item->goal_anim_state = BATON_WAIT;
					else
						item->goal_anim_state = BATON_STOP;
				}
				break;
			}

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

			else if (baton->mood == ESCAPE_MOOD)
				{
					if (lara.target != item && info.ahead && !item->hit_status)
						item->goal_anim_state = BATON_STOP;
					else
						item->goal_anim_state = BATON_RUN;
				}
			else if (baton->mood == BORED_MOOD || ((item->ai_bits & FOLLOW ) && (baton->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 = BATON_STOP;
				else
					item->goal_anim_state = BATON_RUN;
			}
			else if (info.bite && info.distance < BATON_ATTACK0_RANGE)
				item->goal_anim_state = BATON_AIM0;
			else if (info.bite && info.distance < BATON_ATTACK1_RANGE)
				item->goal_anim_state = BATON_AIM1;
			else if (info.bite && info.distance < BATON_WALK_RANGE)
				item->goal_anim_state = BATON_WALK;
			else
				item->goal_anim_state = BATON_RUN;
			break;
		/* - Moved to above BATON-STOP
		case BATON_WAIT:
			if (info.ahead)
				head = info.angle;
			baton->maximum_turn = 0;

			if (baton->mood != BORED_MOOD)
				item->goal_anim_state = BATON_STOP;
			else if (GetRandomControl() < BATON_WALK_CHANCE)
			{
				item->required_anim_state = BATON_WALK;
				item->goal_anim_state = BATON_STOP;
			}
			break;
		*/
		case BATON_WALK:
			head=lara_info.angle;
			baton->flags = 0;

			baton->maximum_turn = BATON_WALK_TURN;

			if (item->ai_bits & PATROL1)
			{
				item->goal_anim_state = BATON_WALK;
				head=0;
			}
			else if (baton->mood == ESCAPE_MOOD)
				item->goal_anim_state = BATON_RUN;
			else if (baton->mood == BORED_MOOD)
			{
				if (GetRandomControl() < BATON_WAIT_CHANCE)
				{
					item->required_anim_state = BATON_WAIT;
					item->goal_anim_state = BATON_STOP;
				}
			}
			else if (info.bite && info.distance < BATON_KICK_RANGE && info.x_angle < 0)
				item->goal_anim_state = BATON_KICK;
			else if (info.bite && info.distance < BATON_ATTACK0_RANGE)
				item->goal_anim_state = BATON_STOP;
			else if (info.bite && info.distance < BATON_ATTACK2_RANGE)
				item->goal_anim_state = BATON_AIM2;
			else //if (!info.ahead || info.distance > BATON_WALK_RANGE)
				item->goal_anim_state = BATON_RUN;
			break;

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

			baton->maximum_turn = BATON_RUN_TURN;
			tilt = angle/2;

			if (item->ai_bits & GUARD)
				item->goal_anim_state = BATON_WAIT;
			else if (baton->mood == ESCAPE_MOOD)
				{
					if (lara.target != item && info.ahead)
						item->goal_anim_state = BATON_STOP;
					break;
				}
			else if ((item->ai_bits & FOLLOW ) && (baton->reached_goal || lara_info.distance > SQUARE(WALL_L*2)))
				item->goal_anim_state = BATON_STOP;	//Maybe BATON_STOP
			else if (baton->mood == BORED_MOOD)
				item->goal_anim_state = BATON_WALK;
			else if (info.ahead && info.distance < BATON_WALK_RANGE)
				item->goal_anim_state = BATON_WALK;
			break;

		case BATON_AIM0:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			baton->maximum_turn = BATON_WALK_TURN;

			baton->flags = 0;
			if (info.bite && info.distance < BATON_ATTACK0_RANGE)
				item->goal_anim_state = BATON_PUNCH0;
			else
				item->goal_anim_state = BATON_STOP;
			break;

		case BATON_AIM1:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			baton->maximum_turn = BATON_WALK_TURN;

			baton->flags = 0;
			if (info.ahead && info.distance < BATON_ATTACK1_RANGE)
				item->goal_anim_state = BATON_PUNCH1;
			else
				item->goal_anim_state = BATON_STOP;
			break;

		case BATON_AIM2:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			baton->maximum_turn = BATON_WALK_TURN;

			baton->flags = 0;
			if (info.bite && info.distance < BATON_ATTACK2_RANGE)
				item->goal_anim_state = BATON_PUNCH2;
			else
				item->goal_anim_state = BATON_WALK;
			break;

		case BATON_PUNCH0:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			baton->maximum_turn = BATON_WALK_TURN;

			if (enemy == lara_item)
			{
				if (!baton->flags && (item->touch_bits & BATON_TOUCH))
				{
					lara_item->hit_points -= BATON_HIT_DAMAGE;
					lara_item->hit_status = 1;
					CreatureEffect(item, &baton_hit, DoBloodSplat);
					SoundEffect(70, &item->pos, 0);

					baton->flags = 1;
				}
			}
			else
			{
				if (!baton->flags && enemy)
				{
					if (ABS(enemy->pos.x_pos - item->pos.x_pos) < BATON_HIT_RADIUS &&
						 ABS(enemy->pos.y_pos - item->pos.y_pos) <= BATON_HIT_RADIUS &&
						 ABS(enemy->pos.z_pos - item->pos.z_pos) < BATON_HIT_RADIUS)
					{
						enemy->hit_points -= BATON_HIT_DAMAGE>>4;
						enemy->hit_status = 1;
						baton->flags = 1;
						CreatureEffect(item, &baton_hit, DoBloodSplat);
						SoundEffect(70, &item->pos, 0);

					}
				}
			}


			break;

		case BATON_PUNCH1:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			baton->maximum_turn = BATON_WALK_TURN;

			if (enemy == lara_item)
			{
				if (!baton->flags && (item->touch_bits & BATON_TOUCH))
				{
					lara_item->hit_points -= BATON_HIT_DAMAGE;
					lara_item->hit_status = 1;
					CreatureEffect(item, &baton_hit, DoBloodSplat);
					SoundEffect(70, &item->pos, 0);

					baton->flags = 1;
				}
			}
			else
			{
				if (!baton->flags && enemy)
				{
					if (ABS(enemy->pos.x_pos - item->pos.x_pos) < BATON_HIT_RADIUS &&
						 ABS(enemy->pos.y_pos - item->pos.y_pos) <= BATON_HIT_RADIUS &&
						 ABS(enemy->pos.z_pos - item->pos.z_pos) < BATON_HIT_RADIUS)
					{
						enemy->hit_points -= BATON_HIT_DAMAGE>>4;
						enemy->hit_status = 1;
						baton->flags = 1;
						CreatureEffect(item, &baton_hit, DoBloodSplat);
						SoundEffect(70, &item->pos, 0);

					}
				}
			}


			if (info.ahead && info.distance > BATON_ATTACK1_RANGE && info.distance < BATON_ATTACK2_RANGE)
				item->goal_anim_state = BATON_PUNCH2;
			break;

		case BATON_PUNCH2:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			baton->maximum_turn = BATON_WALK_TURN;

			if (enemy == lara_item)
			{
				if (baton->flags!=2 && (item->touch_bits & BATON_TOUCH))
				{
					lara_item->hit_points -= BATON_SWIPE_DAMAGE;
					lara_item->hit_status = 1;
					CreatureEffect(item, &baton_hit, DoBloodSplat);
					baton->flags = 2;
					SoundEffect(70, &item->pos, 0);

				}
			}
			else
			{
				if (baton->flags!=2 && enemy)
				{
					if (ABS(enemy->pos.x_pos - item->pos.x_pos) < BATON_HIT_RADIUS &&
						 ABS(enemy->pos.y_pos - item->pos.y_pos) <= BATON_HIT_RADIUS &&
						 ABS(enemy->pos.z_pos - item->pos.z_pos) < BATON_HIT_RADIUS)
					{
						enemy->hit_points -= BATON_SWIPE_DAMAGE>>4;
						enemy->hit_status = 1;
						baton->flags = 2;
						CreatureEffect(item, &baton_hit, DoBloodSplat);
						SoundEffect(70, &item->pos, 0);

					}
				}
			}


			break;

		case BATON_KICK:
			if (info.ahead)
			{
				torso_y = info.angle;
			//	torso_x = info.x_angle;
			}
			baton->maximum_turn = BATON_WALK_TURN;

			if (enemy == lara_item)
			{
				if (baton->flags!=1 && (item->touch_bits & BATON_KICK_TOUCH) && (item->frame_number > anims[item->anim_number].frame_base + 8))
				{
					lara_item->hit_points -= BATON_KICK_DAMAGE;
					lara_item->hit_status = 1;
					CreatureEffect(item, &baton_kick, DoBloodSplat);
					SoundEffect(70, &item->pos, 0);

					baton->flags = 1;
				}
			}
			else
			{
				if (!baton->flags!=1 && enemy && (item->frame_number > anims[item->anim_number].frame_base + 8))
				{
					if (ABS(enemy->pos.x_pos - item->pos.x_pos) < BATON_HIT_RADIUS &&
						 ABS(enemy->pos.y_pos - item->pos.y_pos) <= BATON_HIT_RADIUS &&
						 ABS(enemy->pos.z_pos - item->pos.z_pos) < BATON_HIT_RADIUS)
					{
						enemy->hit_points -= BATON_KICK_DAMAGE>>4;
						enemy->hit_status = 1;
						baton->flags = 1;
						CreatureEffect(item, &baton_kick, DoBloodSplat);
						SoundEffect(70, &item->pos, 0);

					}
				}
			}


			break;

		}
	}

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

	#ifdef DEBUG_BATON
//	sprintf(exit_message, "Torso_y: %d, Torso_x: %d, Head: %d", torso_y, torso_x, head);
//	PrintDbug(2, 2, exit_message);
//	sprintf(exit_message, "Max Turn: %d", baton->maximum_turn);
//	PrintDbug(2, 2, exit_message);
//	sprintf(exit_message, "Angle: %d", angle);
//	PrintDbug(2, 2, exit_message);
	#endif


	/* Actually do animation allowing for collisions */
	if (item->current_anim_state < BATON_DEATH) // Know CLIMB3 marks the start of the CLIMB states
	{
		switch (CreatureVault(item_number, angle, 2, BATON_VAULT_SHIFT))
		{
		case 2:
			/* Half block jump */
			baton->maximum_turn = 0;
			item->anim_number = objects[MP1].anim_index + BATON_CLIMB1_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BATON_CLIMB1;
			break;

		case 3:
			/* 3/4 block jump */
			baton->maximum_turn = 0;
			item->anim_number = objects[MP1].anim_index + BATON_CLIMB2_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BATON_CLIMB2;
			break;

		case 4:
			/* Full block jump */
			baton->maximum_turn = 0;
			item->anim_number = objects[MP1].anim_index + BATON_CLIMB3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BATON_CLIMB3;
			break;
		case -4:
			/* Full block fall */
			baton->maximum_turn = 0;
			item->anim_number = objects[MP1].anim_index + BATON_FALL3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = BATON_FALL3;
			break;
		}
	}
	else
	{
		baton->maximum_turn = 0;
		CreatureAnimation(item_number, angle, 0);
	}
}


