#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 "control.h"
#include "box.h"
#include "effect2.h"


extern int compy_scared_timer;
extern int compys_attack_lara;
extern 	sint16	CarcassItem;

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

void InitialiseCompy(sint16 item_number);
void CompyControl(sint16 item_number);
void CarcassControl(sint16 item_number);

void *func[] __attribute__((section(".header"))) = {
	&InitialiseCompy,
	&CompyControl,
	&CarcassControl
};

#endif


/*********************************** TYPE DEFINITIONS ****************************************/
//Compy - coded by TS 15-7-98
//Carcass added by Gibby on 5/10/98.

#define COMPY_DAMAGE 90

enum compy_anims {COMPY_STOP, COMPY_RUN, COMPY_JUMP, COMPY_ATTACK, COMPY_DEATH};

#define COMPY_RUN_TURN (ONE_DEGREE*10)
#define COMPY_STOP_TURN (ONE_DEGREE*3)

#define COMPY_UPSET_SPEED 15
#define COMPY_HIT_RANGE SQUARE(WALL_L/3)
#define COMPY_ATTACK_ANGLE 0x3000
#define COMPY_JUMP_CHANCE 0x1000
#define COMPY_ATTACK_CHANCE 0x1F

#define COMPY_TOUCH 0x0004

#define COMPY_DIE_ANIM 6
#define COMPY_HIT_FLAG 1

static BITE_INFO compy_hit = {0,0,0,2};

//#define DEBUG_COMPY

#ifdef DEBUG_COMPY
extern char exit_message[];
#endif

#ifdef DEBUG_COMPY
static char *CompyStrings[] = {
	"STOP",
	"RUN",
	"JUMP",
	"ATTACK",
	"DEATH"
};
#endif



void InitialiseCompy(sint16 item_number)
{
	/* Compys start off non-aggresive*/

	InitialiseCreature(item_number);
	compys_attack_lara = 0;
}


void CompyControl(sint16 item_number)
{
	ITEM_INFO *item, *enemy;
	CREATURE_INFO *compy;
	sint16 angle, head, neck, tilt, random, room_number, compys_carcass, linknum;
	sint32 dx, dz;
	AI_INFO info;
	FLOOR_INFO *floor;

	if (!CreatureActive(item_number))
		return;

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

	if (!item->item_flags[1])
	{
		linknum = room[ item->room_number ].item_number;	// Find the item_number for the carcass
		if ( items[linknum].object_number == ANIMATING6 )
			item->item_flags[1] = linknum;
		else
		{
			for ( ; linknum!=NO_ITEM; linknum=items[linknum].next_item )
			{
				if ( items[linknum].object_number == ANIMATING6 )
				{
					item->item_flags[1] = linknum;
					break;
				}
			}
		}
	}

	compys_carcass = item->item_flags[1];

	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != COMPY_DEATH)
		{
			item->anim_number = objects[item->object_number].anim_index + COMPY_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = COMPY_DEATH;
		}
	}
	else
	{

		CreatureAIInfo(item, &info);
		enemy = compy->enemy;
		if (compy->mood == BORED_MOOD && compys_carcass)
		{
			dx =  items[compys_carcass].pos.x_pos - item->pos.x_pos;
			dz =  items[compys_carcass].pos.z_pos - item->pos.z_pos;
			info.distance = (dx * dx) + (dz * dz);
			info.angle =  phd_atan(dz, dx) - item->pos.y_rot;
			info.ahead = (info.angle > -FRONT_ARC && info.angle < FRONT_ARC);
		}

		random = ((item_number & 0x7)*0x200) - 0x700; //so each compy aims for a *slightly* different place & used to stop anims being in phase

	//	GetCreatureMood(item, &info, VIOLENT);
		if (!compy_scared_timer && !compys_attack_lara && ((info.enemy_facing < COMPY_ATTACK_ANGLE && info.enemy_facing > -COMPY_ATTACK_ANGLE && lara_item->speed > COMPY_UPSET_SPEED) || lara_item->current_anim_state == AS_ROLL || item->hit_status))
		{
			item->item_flags[0] = (random + 0x700)>>7;
			compy_scared_timer = 280;		//Scared for less time the more Compys there are - adjust this when we've got lots of them
		}
		else if (compy_scared_timer)
		{
			if (item->item_flags[0] > 0)
				item->item_flags[0]--;
			else
			{
				compy->mood = ESCAPE_MOOD;
				compy_scared_timer--;
			}

			if (GetRandomControl() < COMPY_ATTACK_CHANCE && item->timer > 180)
				compys_attack_lara = 1;
		}
		else if (info.zone_number != info.enemy_zone)
			compy->mood = BORED_MOOD;
		else
			compy->mood = ATTACK_MOOD;		//Temporary frig

		switch (compy->mood)
		{
		case ATTACK_MOOD:
//			random = (GetRandomControl() & 0x800) - 400;
			compy->target.x = enemy->pos.x_pos + (WALL_L * phd_sin(item->pos.y_rot + random) >> W2V_SHIFT);
			compy->target.y = enemy->pos.y_pos;
			compy->target.z = enemy->pos.z_pos + (WALL_L * phd_cos(item->pos.y_rot + random) >> W2V_SHIFT);
			break;
		case STALK_MOOD:
		case ESCAPE_MOOD:	//Turn & Run
			compy->target.x = item->pos.x_pos + (WALL_L * phd_sin(info.angle + 0x8000 + random) >> W2V_SHIFT);
			compy->target.z = item->pos.z_pos + (WALL_L * phd_cos(info.angle + 0x8000 + random) >> W2V_SHIFT);
			room_number = item->room_number;
			floor = GetFloor(compy->target.x, item->pos.y_pos, compy->target.z, &room_number);

			if (abs(boxes[floor->box].height - item->pos.y_pos) > STEP_L)
			{
				compy->mood = BORED_MOOD;
				item->item_flags[0] = compy_scared_timer;
			}
			break;
		case BORED_MOOD:
//			random = (GetRandomControl() & 0x800) - 400;
			if (compys_carcass)
			{
				compy->target.x = items[compys_carcass].pos.x_pos;
				compy->target.z = items[compys_carcass].pos.z_pos;
			}

			break;
		}


		angle = CreatureTurn(item, compy->maximum_turn);
		if (info.ahead)
			head = info.angle;
		neck = -(info.angle/4);

		item->timer++;
		//Compys get angry like monks
		if (item->hit_status && item->timer > 200 && GetRandomControl() < COMPY_ATTACK_CHANCE*100) compys_attack_lara = 1;


	#ifdef DEBUG_COMPY
		if (compy->hurt_by_lara)
		{
	sprintf(exit_message, "Carcass:%d, Z:%d, LZ:%d", compys_carcass, info.zone_number, info.enemy_zone);
	PrintDbug(2, 1, exit_message);
	sprintf(exit_message, "Mood:%d", compy->mood);
	PrintDbug(2, 2, exit_message);
	sprintf(exit_message, "%s", CompyStrings[item->current_anim_state]);
	PrintDbug(2, 3, exit_message);
	sprintf(exit_message, "%s", CompyStrings[item->goal_anim_state]);
	PrintDbug(2, 4, exit_message);

//	sprintf(exit_message, "%s", CobraStrings[item->required_anim_state]);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Anim Number; %d", item->anim_number - objects[COBRA].anim_index);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Alert:%d, Goal:%d, Hurt:%d", cobra->alerted, cobra->reached_goal, cobra->hurt_by_lara);
//	PrintDbug(2,6, exit_message);
//	sprintf(exit_message, "AI Bits: %d", item->ai_bits);
//	PrintDbug(2,7, exit_message);
		}
	#endif

		switch (item->current_anim_state)
		{
			case COMPY_STOP:
				compy->maximum_turn = COMPY_STOP_TURN;
				compy->flags &= ~COMPY_HIT_FLAG;
				if (compy->mood == ATTACK_MOOD)
				{
				/*if (info.angle < COMPY_ATTACK_ANGLE && info.angle > -COMPY_ATTACK_ANGLE && info.distance < COMPY_HIT_RANGE)*/
					if (info.ahead && info.distance < COMPY_HIT_RANGE*4)
					{
						if (compys_attack_lara)
						{
							if (GetRandomControl() < 0x4000)
								item->goal_anim_state = COMPY_ATTACK;
							else
								item->goal_anim_state = COMPY_JUMP;
						}
						else
							item->goal_anim_state = COMPY_STOP;
				//	else if (GetRandomControl() < COMPY_JUMP_CHANCE /*|| (info.ahead && info.distance < COMPY_HIT_RANGE*4)*/)
				//		item->goal_anim_state = COMPY_JUMP;
					}
					else if (info.distance > COMPY_HIT_RANGE*(9 - 4*compys_attack_lara))
						item->goal_anim_state = COMPY_RUN;

				}
				else if (compy->mood == BORED_MOOD)
				{
				/*if (info.angle < COMPY_ATTACK_ANGLE && info.angle > -COMPY_ATTACK_ANGLE && info.distance < COMPY_HIT_RANGE)*/
					if (info.ahead && info.distance < COMPY_HIT_RANGE*3 && compys_carcass)
					{
						if (GetRandomControl() < 0x4000)
							item->goal_anim_state = COMPY_ATTACK;
						else
							item->goal_anim_state = COMPY_JUMP;

				//	else if (GetRandomControl() < COMPY_JUMP_CHANCE /*|| (info.ahead && info.distance < COMPY_HIT_RANGE*4)*/)
				//		item->goal_anim_state = COMPY_JUMP;
					}
					else if (info.distance > COMPY_HIT_RANGE*3)
						item->goal_anim_state = COMPY_RUN;

				}

				else //Anything other than ATTACK mood or BORED mood
				{
					if (GetRandomControl() < COMPY_JUMP_CHANCE)
						item->goal_anim_state = COMPY_JUMP;
					else
						item->goal_anim_state = COMPY_RUN;
				}

				break;

			case COMPY_RUN:
				compy->flags &= ~COMPY_HIT_FLAG;
				compy->maximum_turn = COMPY_RUN_TURN;


				if (info.angle < COMPY_ATTACK_ANGLE && info.angle > -COMPY_ATTACK_ANGLE && info.distance < COMPY_HIT_RANGE*(9 - 4*compys_attack_lara))
				{
					item->goal_anim_state = COMPY_STOP;

				}
				break;

			case COMPY_ATTACK:
			case COMPY_JUMP:
					compy->maximum_turn = COMPY_RUN_TURN;

					if (!(compy->flags & COMPY_HIT_FLAG) && (item->touch_bits & COMPY_TOUCH) && compys_attack_lara)// &&
//						compy->flags >= compy_hitframes[item->current_anim_state][0] &&
//						compy->flags <= compy_hitframes[item->current_anim_state][1])
					{
						compy->flags |= COMPY_HIT_FLAG;
						lara_item->hit_points -= COMPY_DAMAGE;
						lara_item->hit_status = 1;

//						SoundEffect(245, &item->pos, 0);
						CreatureEffect(item, &compy_hit, DoBloodSplat);
					}
					else if (!(compy->flags & COMPY_HIT_FLAG) && info.distance < COMPY_HIT_RANGE && info.ahead && compys_carcass && compy->mood != ATTACK_MOOD)// &&
//						compy->flags >= compy_hitframes[item->current_anim_state][0] &&
//						compy->flags <= compy_hitframes[item->current_anim_state][1])
					{
						compy->flags |= COMPY_HIT_FLAG;
//						SoundEffect(245, &item->pos, 0);
						CreatureEffect(item, &compy_hit, DoBloodSplat);
					}

				break;

		}
	}
	tilt = angle>>1;
	CreatureTilt(item, tilt);
	CreatureJoint(item, 0, head);
	CreatureJoint(item, 1, neck);
//	if (compy->neck_rotation > 0x2000) compy->neck_rotation = 0x2000;
//	else if (compy->neck_rotation < -0x2000) compy->neck_rotation = -0x2000;



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


void CarcassControl(sint16 item_number)
{
	ITEM_INFO 	*item;
	FLOOR_INFO	*floor;
	sint16 		room_number,old_room,maxfs;
	long			h;

	item = &items[item_number];

	if (item->status != ACTIVE)
		return;

	old_room = item->room_number;

	item->pos.y_pos += item->fallspeed;

	room_number = item->room_number;
	floor = GetFloor(item->pos.x_pos,item->pos.y_pos,item->pos.z_pos,&room_number);
	h = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);

	if (item->room_number != room_number)
		ItemNewRoom(item_number, room_number);

	if (item->pos.y_pos >= h)
	{
		item->pos.y_pos = h;
		item->fallspeed = 0;
		item->pos.z_rot = 0x4000;
		return;
	}

	item->pos.z_rot += item->fallspeed;
	item->fallspeed += (room[room_number].flags & UNDERWATER) ? 1:8;
	maxfs = (room[old_room].flags & UNDERWATER) ? 64:512;
	if (item->fallspeed > maxfs)
		item->fallspeed = maxfs;

	if ((room[room_number].flags & UNDERWATER) && !(room[old_room].flags & UNDERWATER))
	{
		splash_setup.x = item->pos.x_pos;
		splash_setup.y = room[room_number].y;
		splash_setup.z = item->pos.z_pos;
		splash_setup.InnerXZoff = 16;
		splash_setup.InnerXZsize = 16;
		splash_setup.InnerYsize = -96;
		splash_setup.InnerXZvel = 0xa0;
		splash_setup.InnerYvel = -item->fallspeed*72;
		splash_setup.InnerGravity = 0x80;
		splash_setup.InnerFriction = 7;
		splash_setup.MiddleXZoff = 24;
		splash_setup.MiddleXZsize = 32;
		splash_setup.MiddleYsize = -64;
		splash_setup.MiddleXZvel = 0xe0;
		splash_setup.MiddleYvel = -item->fallspeed*36;
		splash_setup.MiddleGravity = 0x48;
		splash_setup.MiddleFriction = 8;
		splash_setup.OuterXZoff = 32;
		splash_setup.OuterXZsize = 32;
		splash_setup.OuterXZvel = 0x110;
		splash_setup.OuterFriction = 9;
		SetupSplash(&splash_setup);
		item->fallspeed = 16;
		item->pos.y_pos = room[room_number].y+1;
	}

// Set Global item number for piranhas.

	if (room[item->room_number].flags & UNDERWATER)
		CarcassItem = item_number;
	else
		CarcassItem = -1;
}
