/*********************************************************************************************/
/*                                                                                           */
/* Punk with a pokey stick                                                                   */
/*                                                                                           */
/*********************************************************************************************/

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

#include "effect2.h"
#include "objects.h"
#include "lara.h"
#include "sound.h"
#include "control.h"
#include "sphere.h"
#include "traps.h"
#include "people.h"
#include "gameflow.h"
#include "title.h"
/*---------------------------------------------------------------------------
 *	Constants
\*--------------------------------------------------------------------------*/

//#define DEBUG_PUNK

#define MAX_TRIGGER_RANGE	0x4000
#define PUNK_HIT_DAMAGE 80
#define PUNK_SWIPE_DAMAGE 100
//#define PUNK_HIT_DAMAGE 4
//#define PUNK_SWIPE_DAMAGE 5


enum punk_anims {PUNK_EMPTY, PUNK_STOP, PUNK_WALK, PUNK_PUNCH2, PUNK_AIM2, PUNK_WAIT, PUNK_AIM1, PUNK_AIM0, PUNK_PUNCH1, PUNK_PUNCH0,
	PUNK_RUN, PUNK_DEATH, PUNK_CLIMB3, PUNK_CLIMB1, PUNK_CLIMB2, PUNK_FALL3};

#define PUNK_WALK_TURN (ONE_DEGREE*5)
#define PUNK_RUN_TURN (ONE_DEGREE*6)

#define PUNK_ATTACK0_RANGE SQUARE(WALL_L/2)
#define PUNK_ATTACK1_RANGE SQUARE(WALL_L)
#define PUNK_ATTACK2_RANGE SQUARE(WALL_L*5/4)

#define PUNK_WALK_RANGE SQUARE(WALL_L)

#define PUNK_WALK_CHANCE 0x100
#define PUNK_WAIT_CHANCE 0x100

#define PUNK_DIE_ANIM 26
#define PUNK_STOP_ANIM 6

#define PUNK_CLIMB1_ANIM 28
#define PUNK_CLIMB2_ANIM 29
#define PUNK_CLIMB3_ANIM 27
#define PUNK_FALL3_ANIM  30

#define PUNK_TOUCH 0x2400

#define PUNK_VAULT_SHIFT 260
#define PUNK_AWARE_DISTANCE SQUARE(WALL_L)

/*---------------------------------------------------------------------------
 *	Externals
\*--------------------------------------------------------------------------*/

#ifdef DEBUG_PUNK
extern char exit_message[];
#endif

/*---------------------------------------------------------------------------
 *	Globals
\*--------------------------------------------------------------------------*/

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

void InitialisePunk(sint16 item_number);
void PunkControl(sint16 item_number);

void *func[] __attribute__((section(".header"))) = {
	&InitialisePunk,
	&PunkControl
};

#endif

/*---------------------------------------------------------------------------
 *	Locals
\*--------------------------------------------------------------------------*/

static BITE_INFO punk_hit = {16,48,320, 13};

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

/*---------------------------------------------------------------------------
 *	Local Functions
\*--------------------------------------------------------------------------*/

static void TriggerPunkFlame(sint16 item_number)
{
	long		size;
	SPARKS	*sptr;
	long		dx,dz;

	dx = lara_item->pos.x_pos - items[item_number].pos.x_pos;
	dz = lara_item->pos.z_pos - items[item_number].pos.z_pos;

	if (dx < -MAX_TRIGGER_RANGE || dx > MAX_TRIGGER_RANGE || dz < -MAX_TRIGGER_RANGE || dz > MAX_TRIGGER_RANGE)
		return;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sR = 255;
	sptr->sG = 48+(GetRandomControl()&31);
	sptr->sB = 48;

	sptr->dR = 192+(GetRandomControl()&63);
	sptr->dG = 128+(GetRandomControl()&63);
	sptr->dB = 32;

	sptr->ColFadeSpeed = 12 + (GetRandomControl()&3);
	sptr->FadeToBlack = 8;
	sptr->sLife = sptr->Life = (GetRandomControl()&7)+24;

	sptr->TransType = COLADD;

	sptr->extras = 0;
	sptr->Dynamic = -1;

	sptr->x = ((GetRandomControl()&15)-8);
	sptr->y = 0;
	sptr->z = ((GetRandomControl()&15)-8);

	sptr->Xvel = ((GetRandomControl()&255)-128);
	sptr->Yvel = -(GetRandomControl()&15)-16;
	sptr->Zvel = ((GetRandomControl()&255)-128);
	sptr->Friction = 5;

	if (GetRandomControl()&1)
	{
		sptr->Gravity = -(GetRandomControl()&31)-16;
		sptr->MaxYvel = -(GetRandomControl()&7)-16;
		sptr->Flags = SP_SCALE|SP_DEF|SP_ROTATE|SP_EXPDEF|SP_ITEM|SP_NODEATTATCH;
		sptr->RotAng = GetRandomControl()&4095;
		if (GetRandomControl()&1)
			sptr->RotAdd = -(GetRandomControl()&15)-16;
		else
			sptr->RotAdd = (GetRandomControl()&15)+16;
	}
	else
	{
		sptr->Flags = SP_SCALE|SP_DEF|SP_EXPDEF|SP_ITEM|SP_NODEATTATCH;
		sptr->Gravity = -(GetRandomControl()&31)-16;
		sptr->MaxYvel = -(GetRandomControl()&7)-16;
	}

	sptr->FxObj = item_number;
	sptr->NodeNumber = SPN_PUNKFLAME;

	sptr->Def = objects[EXPLOSION1].mesh_index;
	sptr->Scalar = 1;
	size = (GetRandomControl()&31)+64;
	sptr->Width = sptr->sWidth = size;
 	sptr->Height = sptr->sHeight = size;
	sptr->dWidth = size>>2;
	sptr->dHeight = size>>2;
}

/*---------------------------------------------------------------------------
 *	Public Functions
\*--------------------------------------------------------------------------*/

void InitialisePunk(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];
	InitialiseCreature(item_number);
//	CurrentLevel = LV_TOWER;

	/* Start Punk in stop pose*/
	item->anim_number = objects[PUNK1].anim_index + PUNK_STOP_ANIM;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = item->goal_anim_state = PUNK_STOP;
}

void PunkControl(sint16 item_number)
{
	//  Punk bloke with pokey stick
	ITEM_INFO *item, *real_enemy;
	CREATURE_INFO *punk;
	PHD_VECTOR	pos1;
	sint16 angle, torso_y, torso_x, head, tilt;
	sint32 lara_dx, lara_dz,rnd;
	AI_INFO info, lara_info;

	if (!CreatureActive(item_number))
		return;

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

	if (item->item_flags[2])
	{
		TriggerPunkFlame(item_number);
		rnd = GetRandomControl();
		pos1.x = punk_hit.x+(rnd&15)-8;
		pos1.y = punk_hit.y+((rnd>>4)&15)-8;
		pos1.z = punk_hit.z+((rnd>>8)&15)-8;
		GetJointAbsPosition( &items[item_number], &pos1, punk_hit.mesh_num);
		TriggerDynamic(pos1.x,pos1.y,pos1.z,13,31-((rnd>>4)&3),24-((rnd>>6)&3),rnd&7);
	}


	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != PUNK_DEATH)
		{
			item->anim_number = objects[PUNK1].anim_index + PUNK_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = PUNK_DEATH;
			punk->LOT.step = STEP_L;
		}
	}
	else
	{
		if (item->ai_bits)
			GetAITarget(punk);
		else
			punk->enemy = lara_item;

		CreatureAIInfo(item, &info);

		if (punk->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;
		}

		if (!punk->alerted && punk->enemy == lara_item)
			punk->enemy = NULL;

		GetCreatureMood(item, &info, VIOLENT);

		CreatureMood(item, &info, VIOLENT);

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

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

		switch (item->current_anim_state)
		{
		case PUNK_WAIT:
			if (punk->alerted || item->goal_anim_state == PUNK_RUN)
			{
				item->goal_anim_state = PUNK_STOP;
				break;
			}

		case PUNK_STOP:
			punk->flags = 0;

			punk->maximum_turn = 0;

			head = lara_info.angle;

			if (item->ai_bits & GUARD)
			{
				head = AIGuard(punk);
				if (!(GetRandomControl() & 0xFF))
				{
					if (item->current_anim_state == PUNK_STOP)
						item->goal_anim_state = PUNK_WAIT;
					else
						item->goal_anim_state = PUNK_STOP;
				}
				break;
			}

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

			else if (punk->mood == ESCAPE_MOOD)
				{
					if (lara.target != item && info.ahead && !item->hit_status)
						item->goal_anim_state = PUNK_STOP;
					else
						item->goal_anim_state = PUNK_RUN;
				}
			else if (punk->mood == BORED_MOOD || ((item->ai_bits & FOLLOW ) && (punk->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 = PUNK_STOP;
				else
					item->goal_anim_state = PUNK_RUN;
			}
			else if (info.bite && info.distance < PUNK_ATTACK0_RANGE)
				item->goal_anim_state = PUNK_AIM0;
			else if (info.bite && info.distance < PUNK_ATTACK1_RANGE)
				item->goal_anim_state = PUNK_AIM1;
			else if (info.bite && info.distance < PUNK_WALK_RANGE)
				item->goal_anim_state = PUNK_WALK;
			else
				item->goal_anim_state = PUNK_RUN;

//			item->goal_anim_state = PUNK_WAIT;	//##
			break;
/*	-	Moved to above PUNK_STOP
		case PUNK_WAIT:
			if (info.ahead)
				head = info.angle;
			punk->maximum_turn = 0;

			if (punk->mood != BORED_MOOD)
				item->goal_anim_state = PUNK_STOP;
			else if (GetRandomControl() < PUNK_WALK_CHANCE)
			{
				item->required_anim_state = PUNK_WALK;
				item->goal_anim_state = PUNK_STOP;
			}
			break;
*/
		case PUNK_WALK:
			head=lara_info.angle;

			punk->maximum_turn = PUNK_WALK_TURN;

			if (item->ai_bits & PATROL1)
			{
				item->goal_anim_state = PUNK_WALK;
				head=0;
			}
			else if (punk->mood == ESCAPE_MOOD)
				item->goal_anim_state = PUNK_RUN;
			else if (punk->mood == BORED_MOOD)
			{
				if (GetRandomControl() < PUNK_WAIT_CHANCE)
				{
					item->required_anim_state = PUNK_WAIT;
					item->goal_anim_state = PUNK_STOP;
				}
			}
			else if (info.bite && info.distance < PUNK_ATTACK0_RANGE)
				item->goal_anim_state = PUNK_STOP;
			else if (info.bite && info.distance < PUNK_ATTACK2_RANGE)
				item->goal_anim_state = PUNK_AIM2;
			else //if (!info.ahead || info.distance > PUNK_WALK_RANGE)
				item->goal_anim_state = PUNK_RUN;
			break;

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

			punk->maximum_turn = PUNK_RUN_TURN;
			tilt = angle/2;

			if (item->ai_bits & GUARD)
				item->goal_anim_state = PUNK_WAIT;
			else if (punk->mood == ESCAPE_MOOD)
				{
					if (lara.target != item && info.ahead)
						item->goal_anim_state = PUNK_STOP;
					break;
				}
			else if ((item->ai_bits & FOLLOW ) && (punk->reached_goal || lara_info.distance > SQUARE(WALL_L*2)))
				item->goal_anim_state = PUNK_STOP;
			else if (punk->mood == BORED_MOOD)
				item->goal_anim_state = PUNK_WALK;
			else if (info.ahead && info.distance < PUNK_WALK_RANGE)
				item->goal_anim_state = PUNK_WALK;
			break;

		case PUNK_AIM0:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			punk->maximum_turn = PUNK_WALK_TURN;

			punk->flags = 0;
			if (info.bite && info.distance < PUNK_ATTACK0_RANGE)
				item->goal_anim_state = PUNK_PUNCH0;
			else
				item->goal_anim_state = PUNK_STOP;
			break;

		case PUNK_AIM1:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			punk->maximum_turn = PUNK_WALK_TURN;

			punk->flags = 0;
			if (info.ahead && info.distance < PUNK_ATTACK1_RANGE)
				item->goal_anim_state = PUNK_PUNCH1;
			else
				item->goal_anim_state = PUNK_STOP;
			break;

		case PUNK_AIM2:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			punk->maximum_turn = PUNK_WALK_TURN;

			punk->flags = 0;
			if (info.bite && info.distance < PUNK_ATTACK2_RANGE)
				item->goal_anim_state = PUNK_PUNCH2;
			else
				item->goal_anim_state = PUNK_WALK;
			break;

		case PUNK_PUNCH0:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			punk->maximum_turn = PUNK_WALK_TURN;

			if (!punk->flags && (item->touch_bits & PUNK_TOUCH))
			{
				lara_item->hit_points -= PUNK_HIT_DAMAGE;
				lara_item->hit_status = 1;
				CreatureEffect(item, &punk_hit, DoBloodSplat);
				SoundEffect(70, &item->pos, 0);

				if (item->item_flags[2] == 1)	// Flaming torch ?
					LaraBurn();
				else if (item->item_flags[2])
					item->item_flags[2]--;
				punk->flags = 1;
			}
			break;

		case PUNK_PUNCH1:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			punk->maximum_turn = PUNK_WALK_TURN;

			if (!punk->flags && (item->touch_bits & PUNK_TOUCH))
			{
				lara_item->hit_points -= PUNK_HIT_DAMAGE;
				lara_item->hit_status = 1;
				CreatureEffect(item, &punk_hit, DoBloodSplat);
				SoundEffect(70, &item->pos, 0);

				if (item->item_flags[2] == 1)	// Flaming torch ?
					LaraBurn();
				else if (item->item_flags[2])
					item->item_flags[2]--;
				punk->flags = 1;
			}

			if (info.ahead && info.distance > PUNK_ATTACK1_RANGE && info.distance < PUNK_ATTACK2_RANGE)
				item->goal_anim_state = PUNK_PUNCH2;
			break;

		case PUNK_PUNCH2:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
			}
			punk->maximum_turn = PUNK_WALK_TURN;

			if (punk->flags!=2 && (item->touch_bits & PUNK_TOUCH))
			{
				lara_item->hit_points -= PUNK_SWIPE_DAMAGE;
				lara_item->hit_status = 1;
				CreatureEffect(item, &punk_hit, DoBloodSplat);
				SoundEffect(70, &item->pos, 0);

				if (item->item_flags[2] == 1)	// Flaming torch ?
					LaraBurn();
				else if (item->item_flags[2])
					item->item_flags[2]--;
				punk->flags = 2;
			}
			break;
		}
	}

	#ifdef DEBUG_PUNK
	sprintf(exit_message, "Bx:%d, LBx:%d, Z:%d, LZ:%d", item->box_number, lara_item->box_number, info.zone_number, info.enemy_zone);
	PrintDbug(2, 1, exit_message);
	sprintf(exit_message, "Mood:%d, MaxTurn:%d", punk->mood, punk->maximum_turn);
	PrintDbug(2, 2, exit_message);
	sprintf(exit_message, "%s", PunkStrings[item->current_anim_state]);
	PrintDbug(2, 3, exit_message);
	sprintf(exit_message, "%s", PunkStrings[item->goal_anim_state]);
	PrintDbug(2, 4, exit_message);
	sprintf(exit_message, "%s", PunkStrings[item->required_anim_state]);
	PrintDbug(2, 5, exit_message);
	sprintf(exit_message, "Anim Number; %d", item->anim_number - objects[PUNK1].anim_index);
	PrintDbug(2, 6, exit_message);

	sprintf(exit_message, "AIbits:%d, Alerted:%d", item->ai_bits, punk->alerted);
	PrintDbug(2,5, exit_message);
	#endif

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

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

		case 3:
			/* 3/4 block jump */
			punk->maximum_turn = 0;
			item->anim_number = objects[PUNK1].anim_index + PUNK_CLIMB2_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = PUNK_CLIMB2;
			break;

		case 4:
			/* Full block jump */
			punk->maximum_turn = 0;
			item->anim_number = objects[PUNK1].anim_index + PUNK_CLIMB3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = PUNK_CLIMB3;
			break;
		case -4:
			/* Full block fall */
			punk->maximum_turn = 0;
			item->anim_number = objects[PUNK1].anim_index + PUNK_FALL3_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = PUNK_FALL3;
			break;
		}
	}
	else
	{
		punk->maximum_turn = 0;
		CreatureAnimation(item_number, angle, 0);
	}
}
