/*********************************************************************************************/
/*                                                                                           */
/* Area 51 Civilian                                                                         */
/*                                                                                           */
/*********************************************************************************************/

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

#include "objects.h"
#include "lara.h"
#include "sound.h"
#include "people.h"
#include "control.h"
#include "effect2.h"
#include "title.h"

void InitialiseCivvy(sint16 item_number);
void CivvyControl(sint16 item_number);
void ControlElectricFence(sint16 item_number);
static void TriggerFenceSparks(long x, long y, long z, long kill);

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

void *func[] = {
	&InitialiseCivvy,
	&CivvyControl,
	&ControlElectricFence,
};

#endif

//#define CIVVY_HIT_DAMAGE 80
//#define CIVVY_SWIPE_DAMAGE 100
#define CIVVY_HIT_DAMAGE 40
#define CIVVY_SWIPE_DAMAGE 50


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

// Area 51 Civilian
BITE_INFO civvy_hit = {0,0,0, 13};

enum civvy_anims {CIVVY_EMPTY, CIVVY_STOP, CIVVY_WALK, CIVVY_PUNCH2, CIVVY_AIM2, CIVVY_WAIT, CIVVY_AIM1, CIVVY_AIM0, CIVVY_PUNCH1, CIVVY_PUNCH0,
	CIVVY_RUN, CIVVY_DEATH, CIVVY_CLIMB3, CIVVY_CLIMB1, CIVVY_CLIMB2, CIVVY_FALL3};

#define CIVVY_WALK_TURN (ONE_DEGREE*5)
#define CIVVY_RUN_TURN (ONE_DEGREE*6)

#define CIVVY_ATTACK0_RANGE SQUARE(WALL_L/3)
#define CIVVY_ATTACK1_RANGE SQUARE(WALL_L*2/3)
#define CIVVY_ATTACK2_RANGE SQUARE(WALL_L)

#define CIVVY_WALK_RANGE SQUARE(WALL_L)
#define CIVVY_ESCAPE_RANGE SQUARE(WALL_L*3)

#define CIVVY_WALK_CHANCE 0x100
#define CIVVY_WAIT_CHANCE 0x100

#define CIVVY_DIE_ANIM 26
#define CIVVY_STOP_ANIM 6

#define CIVVY_CLIMB1_ANIM 28
#define CIVVY_CLIMB2_ANIM 29
#define CIVVY_CLIMB3_ANIM 27
#define CIVVY_FALL3_ANIM  30

#define CIVVY_TOUCH 0x2400

#define CIVVY_VAULT_SHIFT 260
#define CIVVY_AWARE_DISTANCE SQUARE(WALL_L)


//#define DEBUG_CIVVY

#ifdef DEBUG_CIVVY
extern char exit_message[];
#endif

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

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

void InitialiseCivvy(sint16 item_number)
{
	ITEM_INFO *item;

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

	/* Start Civvy in stop pose*/
	item->anim_number = objects[CIVVIE].anim_index + CIVVY_STOP_ANIM;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = item->goal_anim_state = CIVVY_STOP;
}

void CivvyControl(sint16 item_number)
{
	// Area 51 - Civvy Man
	ITEM_INFO *item, *real_enemy;
	CREATURE_INFO *civvy;
	sint16 angle, torso_y, torso_x, head, tilt;
	sint32 lara_dx, lara_dz;
	AI_INFO info, lara_info;

	if (!CreatureActive(item_number))
		return;

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


	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 != CIVVY_DEATH)
		{
			item->anim_number = objects[CIVVIE].anim_index + CIVVY_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = CIVVY_DEATH;
			civvy->LOT.step = STEP_L;
		}
	}
	else
	{
		if (item->ai_bits)
			GetAITarget(civvy);
		else
			civvy->enemy = lara_item;

		CreatureAIInfo(item, &info);

		if (civvy->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);

		//## TS - test hide code
		if (civvy->enemy == lara_item && info.distance > CIVVY_ESCAPE_RANGE && info.enemy_facing < 0x3000 && info.enemy_facing > -0x3000)		
			civvy->mood = ESCAPE_MOOD;

		CreatureMood(item, &info, VIOLENT);


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

	#ifdef DEBUG_CIVVY
	sprintf(exit_message, "Anim Number: %d", 	item->anim_number - objects[CIVVIE].anim_index);
	PrintDbug(2, 5, exit_message);
	#endif


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

		switch (item->current_anim_state)
		{
		case CIVVY_WAIT:
			if (civvy->alerted || item->goal_anim_state == CIVVY_RUN)
			{
				item->goal_anim_state = CIVVY_STOP;
				break;
			}

		case CIVVY_STOP:
			civvy->flags = 0;

			civvy->maximum_turn = 0;

			head = lara_info.angle;

			if (item->ai_bits & GUARD)
			{
				head = AIGuard(civvy);
				if (!(GetRandomControl() & 0xFF))
				{
					if (item->current_anim_state == CIVVY_STOP)
						item->goal_anim_state = CIVVY_WAIT;
					else
						item->goal_anim_state = CIVVY_STOP;
				}
				break;
			}

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

			else if (civvy->mood == ESCAPE_MOOD)
				{
					if (lara.target != item && info.ahead)
						item->goal_anim_state = CIVVY_STOP;
					else
						item->goal_anim_state = CIVVY_RUN;
				}
			else if (civvy->mood == BORED_MOOD || ((item->ai_bits & FOLLOW ) && (civvy->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 = CIVVY_STOP;
				else
					item->goal_anim_state = CIVVY_RUN;
			}
			else if (info.bite && info.distance < CIVVY_ATTACK0_RANGE)
				item->goal_anim_state = CIVVY_AIM0;
			else if (info.bite && info.distance < CIVVY_ATTACK1_RANGE)
				item->goal_anim_state = CIVVY_AIM1;
			else if (info.bite && info.distance < CIVVY_WALK_RANGE)
				item->goal_anim_state = CIVVY_WALK;
			else
				item->goal_anim_state = CIVVY_RUN;
			break;
		/* - Moved to above CIVVY-STOP
		case CIVVY_WAIT:
			if (info.ahead)
				head = info.angle;
			civvy->maximum_turn = 0;

			if (civvy->mood != BORED_MOOD)
				item->goal_anim_state = CIVVY_STOP;
			else if (GetRandomControl() < CIVVY_WALK_CHANCE)
			{
				item->required_anim_state = CIVVY_WALK;
				item->goal_anim_state = CIVVY_STOP;
			}
			break;
		*/
		case CIVVY_WALK:
			head=lara_info.angle;

			civvy->maximum_turn = CIVVY_WALK_TURN;

			if (item->ai_bits & PATROL1)
			{
				item->goal_anim_state = CIVVY_WALK;
				head=0;
			}
			else if (civvy->mood == ESCAPE_MOOD)
				item->goal_anim_state = CIVVY_RUN;
			else if (civvy->mood == BORED_MOOD)
			{
				if (GetRandomControl() < CIVVY_WAIT_CHANCE)
				{
					item->required_anim_state = CIVVY_WAIT;
					item->goal_anim_state = CIVVY_STOP;
				}
			}
			else if (info.bite && info.distance < CIVVY_ATTACK0_RANGE)
				item->goal_anim_state = CIVVY_STOP;
			else if (info.bite && info.distance < CIVVY_ATTACK2_RANGE)
				item->goal_anim_state = CIVVY_AIM2;
			else //if (!info.ahead || info.distance > CIVVY_WALK_RANGE)
				item->goal_anim_state = CIVVY_RUN;
			break;

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

			civvy->maximum_turn = CIVVY_RUN_TURN;
			tilt = angle/2;

			if (item->ai_bits & GUARD)
				item->goal_anim_state = CIVVY_WAIT;
			else if (civvy->mood == ESCAPE_MOOD)
				{
					if (lara.target != item && info.ahead)
						item->goal_anim_state = CIVVY_STOP;
					break;
				}
			else if ((item->ai_bits & FOLLOW ) && (civvy->reached_goal || lara_info.distance > SQUARE(WALL_L*2)))
				item->goal_anim_state = CIVVY_STOP;	//Maybe CIVVY_STOP
			else if (civvy->mood == BORED_MOOD)
				item->goal_anim_state = CIVVY_WALK;
			else if (info.ahead && info.distance < CIVVY_WALK_RANGE)
				item->goal_anim_state = CIVVY_WALK;
			break;

		case CIVVY_AIM0:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			civvy->maximum_turn = CIVVY_WALK_TURN;

			civvy->flags = 0;
			if (info.bite && info.distance < CIVVY_ATTACK0_RANGE)
				item->goal_anim_state = CIVVY_PUNCH0;
			else
				item->goal_anim_state = CIVVY_STOP;
			break;

		case CIVVY_AIM1:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			civvy->maximum_turn = CIVVY_WALK_TURN;

			civvy->flags = 0;
			if (info.ahead && info.distance < CIVVY_ATTACK1_RANGE)
				item->goal_anim_state = CIVVY_PUNCH1;
			else
				item->goal_anim_state = CIVVY_STOP;
			break;

		case CIVVY_AIM2:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			civvy->maximum_turn = CIVVY_WALK_TURN;

			civvy->flags = 0;
			if (info.bite && info.distance < CIVVY_ATTACK2_RANGE)
				item->goal_anim_state = CIVVY_PUNCH2;
			else
				item->goal_anim_state = CIVVY_WALK;
			break;

		case CIVVY_PUNCH0:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			civvy->maximum_turn = CIVVY_WALK_TURN;

			if (!civvy->flags && (item->touch_bits & CIVVY_TOUCH))
			{
				lara_item->hit_points -= CIVVY_HIT_DAMAGE;
				lara_item->hit_status = 1;
				CreatureEffect(item, &civvy_hit, DoBloodSplat);
				SoundEffect(70, &item->pos, 0);

				civvy->flags = 1;
			}
			break;

		case CIVVY_PUNCH1:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			civvy->maximum_turn = CIVVY_WALK_TURN;

			if (!civvy->flags && (item->touch_bits & CIVVY_TOUCH))
			{
				lara_item->hit_points -= CIVVY_HIT_DAMAGE;
				lara_item->hit_status = 1;
				CreatureEffect(item, &civvy_hit, DoBloodSplat);
				SoundEffect(70, &item->pos, 0);

				civvy->flags = 1;
			}

			if (info.ahead && info.distance > CIVVY_ATTACK1_RANGE && info.distance < CIVVY_ATTACK2_RANGE)
				item->goal_anim_state = CIVVY_PUNCH2;
			break;

		case CIVVY_PUNCH2:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			civvy->maximum_turn = CIVVY_WALK_TURN;

			if (civvy->flags!=2 && (item->touch_bits & CIVVY_TOUCH))
			{
				lara_item->hit_points -= CIVVY_SWIPE_DAMAGE;
				lara_item->hit_status = 1;
				CreatureEffect(item, &civvy_hit, DoBloodSplat);
				SoundEffect(70, &item->pos, 0);

				civvy->flags = 2;
			}
			break;
		}
	}

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

	#ifdef DEBUG_CIVVY
//	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", civvy->maximum_turn);
//	PrintDbug(2, 2, exit_message);
//	sprintf(exit_message, "Angle: %d", angle);
//	PrintDbug(2, 2, exit_message);
	#endif

	#ifdef DEBUG_CIVVY
//	sprintf(exit_message, "Mood: %d", civvy->mood);
//	PrintDbug(2, 3, exit_message);
	sprintf(exit_message, "%s", CivvyStrings[item->current_anim_state]);
	PrintDbug(2, 3, exit_message);
	sprintf(exit_message, "%s", CivvyStrings[item->goal_anim_state]);
	PrintDbug(2, 4, exit_message);
	sprintf(exit_message, "Md: %d, Z:%d, LZ:%d, B:%d, LB:%d", civvy->mood, info.zone_number, info.enemy_zone, item->box_number, lara_item->box_number);
	PrintDbug(2, 2, exit_message);
//	sprintf(exit_message, "%s", CivvyStrings[item->required_anim_state]);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Anim Number; %d", item->anim_number - objects[CIVVIE].anim_index);
//	PrintDbug(2, 6, exit_message);
	sprintf(exit_message, "Alert:%d, Goal:%d, Hurt:%d", civvy->alerted, civvy->reached_goal, civvy->hurt_by_lara);
	PrintDbug(2,6, exit_message);

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

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

		case 3:
			/* 3/4 block jump */
			civvy->maximum_turn = 0;
			item->anim_number = objects[CIVVIE].anim_index + CIVVY_CLIMB2_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = CIVVY_CLIMB2;
			break;

		case 4:
			/* Full block jump */
			civvy->maximum_turn = 0;
			item->anim_number = objects[CIVVIE].anim_index + CIVVY_CLIMB3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = CIVVY_CLIMB3;
			break;
		case -4:
			/* Full block fall */
			civvy->maximum_turn = 0;
			item->anim_number = objects[CIVVIE].anim_index + CIVVY_FALL3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = CIVVY_FALL3;
			break;
		}
	}
	else
	{
		civvy->maximum_turn = 0;
		CreatureAnimation(item_number, angle, 0);
	}
}


#define FENCE_WIDTH		128
#define FENCE_LENGTH	1024+32

void ControlElectricFence(sint16 item_number)
{
	ITEM_INFO *item;
	long		x,z,xsize,zsize;
	long		dx,dz,tx,ty,tz,xand,zand;

	item = &items[item_number];

	if (!TriggerActive(item))
		return;

	dx = lara_item->pos.x_pos - item->pos.x_pos;
	dz = lara_item->pos.z_pos - item->pos.z_pos;

	if (dx < -0x5000 || dx > 0x5000 || dz < -0x5000 || dz > 0x5000)
		return;

	switch (item->pos.y_rot)
	{
		case 0:
			x = item->pos.x_pos + 512;
			z = item->pos.z_pos + 512;
			tx = x-FENCE_LENGTH;
			tz = z-256;
			xand = 2047;
			zand = 0;
			xsize = FENCE_LENGTH;
			zsize = FENCE_WIDTH;
			break;

		case 16384:
			x = item->pos.x_pos + 512;
			z = item->pos.z_pos - 512;
			tx = x-256;
			tz = z-FENCE_LENGTH;
			xand = 0;
			zand = 2047;
			xsize = FENCE_WIDTH;
			zsize = FENCE_LENGTH;
			break;

		case -32768:
			x = item->pos.x_pos - 512;
			z = item->pos.z_pos - 512;
			tx = x-FENCE_LENGTH;
			tz = z+256;
			xand = 2047;
			zand = 0;
			xsize = FENCE_LENGTH;
			zsize = FENCE_WIDTH;
			break;

		case -16384:
			x = item->pos.x_pos - 512;
			z = item->pos.z_pos + 512;
			tx = x+256;
			tz = z-FENCE_LENGTH;
			xand = 0;
			zand = 2047;
			xsize = FENCE_WIDTH;
			zsize = FENCE_LENGTH;
			break;

		default:
			x = z = xsize = zsize = tx = tz = xand = zand = 0;
			break;
	}

	if ((GetRandomControl()&63) == 0)
	{
		long	lp,cnt;

		cnt = (GetRandomControl()&3)+3;
		if (xand)
			tx += (GetRandomControl()&xand);
		else
			tz += (GetRandomControl()&zand);

		if (CurrentLevel != LV_OFFICE)
			ty = item->pos.y_pos-(GetRandomControl()&2047)-(GetRandomControl()&1023);
		else
			ty = item->pos.y_pos - (GetRandomControl()&0x1F);

		for (lp=0;lp<cnt;lp++)
		{
			TriggerFenceSparks(tx,ty,tz,0);
			if (xand)
				tx += ((GetRandomControl()&xand)&7)-4;
			else
				tz += ((GetRandomControl()&zand)&7)-4;
			ty += (GetRandomControl()&7)-4;
		}
	}

	if ( lara.electric || CurrentLevel == LV_OFFICE ||
		lara_item->pos.x_pos < x-xsize || lara_item->pos.x_pos > x+xsize ||
		lara_item->pos.z_pos < z-zsize || lara_item->pos.z_pos > z+zsize ||
		lara_item->pos.y_pos > item->pos.y_pos + 32 || lara_item->pos.y_pos < item->pos.y_pos - 3072)
		return;

	{
		long	lp,cnt,lp2,cnt2,sx,sz;

		sx = tx;
		sz = tz;

		cnt = (GetRandomControl()&15)+3;
		for (lp=0;lp<cnt;lp++)
		{
			if (xand)
				tx = lara_item->pos.x_pos + (GetRandomControl()&511) - 256;
			else
				tz = lara_item->pos.z_pos + (GetRandomControl()&511) - 256;
			ty = lara_item->pos.y_pos - (GetRandomControl()%768);

			cnt2 = (GetRandomControl()&3)+6;
			for (lp2=0;lp2<cnt2;lp2++)
			{
				TriggerFenceSparks(tx,ty,tz,1);
				if (xand)
					tx += ((GetRandomControl()&xand)&7)-4;
				else
					tz += ((GetRandomControl()&zand)&7)-4;
				ty += (GetRandomControl()&7)-4;
			}
			tx = sx;
			tz = sz;
		}
	}

	lara.electric = 1;
	lara_item->hit_points = 0;
}

static void TriggerFenceSparks(long x, long y, long z, long kill)
{
	SPARKS	*sptr;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sB = (GetRandomControl()&63)+192;
	sptr->sR = sptr->sB;
	sptr->sG = sptr->sB;

	sptr->dB = (GetRandomControl()&63)+192;
	sptr->dR = sptr->sB>>2;
	sptr->dG = sptr->sB>>1;

	sptr->ColFadeSpeed = 8;
	sptr->FadeToBlack = 16;
	sptr->sLife = sptr->Life = 32+(GetRandomControl()&7);
	sptr->TransType = COLADD;
	sptr->Dynamic = -1;

	sptr->x = x;
	sptr->y = y;
	sptr->z = z;
	sptr->Xvel = ((GetRandomControl()&255)-128)<<1;
	sptr->Yvel = (GetRandomControl()&15)-8-(kill<<5);
	sptr->Zvel = ((GetRandomControl()&255)-128)<<1;

	sptr->Friction = 4;//|(4<<4);
	sptr->Flags = SP_SCALE;
	sptr->Scalar = 1+kill;
	sptr->Width = sptr->sWidth = (GetRandomControl()&3)+4;
	sptr->dWidth = sptr->sWidth = 1;
	sptr->Height = sptr->sHeight = sptr->Width;
	sptr->dHeight = sptr->dWidth;
	sptr->Gravity = 16+(GetRandomControl()&15);
	sptr->MaxYvel = 0;
}


