//////////////////////////////////////////////////////////////////////
//	the Lab Monkey!!!
//
//	TODO:	make ai switchable for monkey bonus!
//	TODO:	more ambient screaming, chest beating, etc.
//////////////////////////////////////////////////////////////////////

#include	"world.h"

#include	"ai.h"
#include	"ai_utils.h"

#include	"ai_move.h"
#include	"ai_weapons.h"
#include	"ai_task.h"
#include	"ai_frames.h"

#include	"skat.h"
#include	"nodelist.h"
#include	"actorlist.h"

////////////////////////////////////////////////////////////////////////////////
// exports
////////////////////////////////////////////////////////////////////////////////

DllExport	void	monster_labmonkey (userEntity_t *self);
DllExport	void	monkey_message (userEntity_t *self);

///////////////////////////////////////////////////////////////////////////////////
// monkey specific funcitons
///////////////////////////////////////////////////////////////////////////////////

void	monkey_close_melee (userEntity_t *self);
void	monkey_far_melee (userEntity_t *self);
void	monkey_jump_attack (userEntity_t *self);

////////////////////////////////
//	monkey_init_frametable
//
////////////////////////////////

void	monkey_init_frametable (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	int				i;

	/////////////////////////////////////////////////////////////////////////////
	//	attacks
	/////////////////////////////////////////////////////////////////////////////
	
	//	overhand attack
	ai_set_sequence_data (self, hook->frames->atak [0], -1, NULL, 10, "monsters/labmonkey/labmonkeyattack1.wav", 0, -1, 0, -1, FRAME_ONCE);
	//	swinging attack
	ai_set_sequence_data (self, hook->frames->atak [1], 9, "monsters/labmonkey/labmonkeyattack1.wav", 17, "monsters/labmonkey/labmonkeyattack1.wav", 0, -1, 0, -1, FRAME_ONCE);
	//	jumping and swinging swinging attack
	ai_set_sequence_data (self, hook->frames->atak [2], 9, "monsters/labmonkey/labmonkeyattack1.wav", 17, "monsters/labmonkey/labmonkeyattack1.wav", 0, -1, 0, -1, FRAME_ONCE);
	//	jumping overhand attack
	ai_set_sequence_data (self, hook->frames->atak [3], -1, NULL, 12, "monsters/labmonkey/labmonkeyattack1.wav", 0, -1, 0, -1, FRAME_ONCE);
	//	big girly-swing attack
	ai_set_sequence_data (self, hook->frames->atak [4], -1, NULL, 12, "monsters/labmonkey/labmonkeyattack1.wav", 0, -1, 0, -1, FRAME_ONCE);

	/////////////////////////////////////////////////////////////////////////////
	//	ambients
	/////////////////////////////////////////////////////////////////////////////

	ai_set_sequence_data (self, hook->frames->stand [0], -1, NULL, -1, NULL, 0, -1, 0, -1, FRAME_LOOP);
	//	scratching under arm
	ai_set_sequence_data (self, hook->frames->stand [1], -1, NULL, -1, NULL, 0, -1, 0, -1, FRAME_ONCE);
	//	screaming with hands raised
	ai_set_sequence_data (self, hook->frames->stand [2], 0, NULL, -1, NULL, 0, -1, 0, -1, FRAME_ONCE);
	//	beating chest
	ai_set_sequence_data (self, hook->frames->stand [3], -1, NULL, -1, NULL, 0, -1, 0, -1, FRAME_ONCE);
	//	beating fists on ground
	ai_set_sequence_data (self, hook->frames->stand [4], -1, NULL, -1, NULL, 0, -1, 0, -1, FRAME_ONCE);

	//	init move_scale for lab monkeys's hopping run
	hook->frames->run [0]->move_scale [0] = 0;	// 1.0 times
	hook->frames->run [0]->move_scale [1] = 127;	// 2.0 times
	hook->frames->run [0]->move_scale [2] = 127;	// 2.0 times
	hook->frames->run [0]->move_scale [3] = 127;	// 2.0 times
	hook->frames->run [0]->move_scale [4] = 127;	// 2.0 times
	hook->frames->run [0]->move_scale [5] = 96;	// 2.0 times
	hook->frames->run [0]->move_scale [6] = 64;	// 2.0 times
	for (i = 7; i < (hook->frames->run [0]->last - hook->frames->run [0]->first); i++)
	{
		if (i < 10)
			hook->frames->run [0]->move_scale [i] = 0;		// 1.0 times
		else
			hook->frames->run [0]->move_scale [i] = -128;		// 0.125 times

	}
}

////////////////////////////////
//	monkey_set_attack_seq
//
////////////////////////////////

void	monkey_set_attack_seq (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	float			dist;
	int				i;
	Vector			temp;
	
	dist = qv_dist (self->s.origin, self->enemy->s.origin);

	if (dist > 108 && hook->dflags & DFL_JUMPATTACK)
	{
		i = (int)floor (rnd () * 2) + 2;
		ai_init_sequence (self, hook->frames->atak [i], SEQUENCE_ATAK, i);

		//	give jumping velocity to monkey here
		temp = Vector (self->s.angles);
		temp.AngleVectors (forward, right, up);

		temp = forward * hook->run_speed * 1.5;
		temp.z = hook->upward_vel;
		temp.set (self->velocity);
		self->groundEntity = NULL;

		self->gravity = 1.0;

		monkey_jump_attack (self);
	}
	else if (dist > 56)
	{
		if (rnd () < 0.5)
			ai_init_sequence (self, hook->frames->atak [0], SEQUENCE_ATAK, 0);
		else
			ai_init_sequence (self, hook->frames->atak [4], SEQUENCE_ATAK, 4);
		monkey_far_melee (self);
	}
	else
	{
		ai_init_sequence (self, hook->frames->atak [1], SEQUENCE_ATAK, 1);
		monkey_close_melee (self);
	}
}

////////////////////////////////////////
//	monkey_hop
//
//	for simplicity's sake, monkey doesn't check where it's hopping
//	also saves us some trace lines
////////////////////////////////////////

void	monkey_hop (userEntity_t *self, float yaw_add)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	float			hop_yaw;
	Vector			hop_angles;
	
	hop_yaw = self->s.angles [1];
	if (rnd () < 0.5)
		hop_yaw += yaw_add;
	else
		hop_yaw -= yaw_add;

	hop_angles = Vector (0, hop_yaw, 0);
	hop_angles.AngleVectors (forward, right, up);

	self->gravity = 2.0;

	forward = forward * 150.0;
	forward.set (self->velocity);

	self->groundEntity = NULL;
	self->velocity [2] = hook->upward_vel * 2;

	ai_init_sequence (self, hook->frames->atak [2], SEQUENCE_ATAK, 2);
}

////////////////////////////////////////
//	monkey_roar
////////////////////////////////////////

void	monkey_roar (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	int				i;

	//	face enemy if still alive
	if (self->enemy->deadflag == DEAD_NO)
		ai_set_goal (self, self->enemy, self->enemy->s.origin, GOAL_ENEMY);

	i = (int) floor (rnd () * 3) + 2;

	// killed the player, so roar
//	ai_set_sequence_sounds (self, hook->frames->stand [i]->soundframe1, hook->frames->stand [i]->sound1, 
//								  hook->frames->stand [i]->soundframe2, hook->frames->stand [i]->sound2);
//	ai_begin_transition (self, hook->frames->stand [i]->first, hook->frames->stand [i]->last, hook->temp_think);
	ai_begin_transition (self, hook->frames->stand [i], SEQUENCE_STAND, i, hook->temp_think);
}

////////////////////////////////////////
//	monkey_roar_killed_enemy
////////////////////////////////////////

void	monkey_roar_killed_enemy (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	// killed the player, so roar
//	ai_set_sequence_sounds (self, hook->frames->stand [2]->soundframe1, hook->frames->stand [2]->sound1, 
//								  hook->frames->stand [2]->soundframe2, hook->frames->stand [2]->sound2);
//	ai_begin_transition (self, hook->frames->stand [2]->first, hook->frames->stand [2]->last, ai_killed_enemy);
	ai_begin_transition (self, hook->frames->stand [2], SEQUENCE_STAND, 2, ai_killed_enemy);
}

///////////////////////////
//	monkey_jump_attack
//
///////////////////////////

void	monkey_jump_attack (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	Vector			temp;
	float			dist;

	hook->dflags |= DFL_FACEENEMY;

	dist = qv_dist (self->s.origin, self->enemy->s.origin);

	if (hook->frame_flags & FRAME_PLAYSOUND1)
	{
		hook->frame_flags = hook->frame_flags - FRAME_PLAYSOUND1;
				
		ai_fire_curWeapon (self, Vector (self->s.origin), Vector (self->enemy->s.origin), 0, 0);
	}
	if (hook->frame_flags & FRAME_PLAYSOUND2)
	{
		hook->frame_flags = hook->frame_flags - FRAME_PLAYSOUND2;
	
		ai_fire_curWeapon (self, Vector (self->s.origin), Vector (self->enemy->s.origin), 0, 0);
	}

	// attack without any transitions
	if (hook->frame_flags & FRAME_STATIC)
	{
		hook->frame_flags -= FRAME_STATIC;	

		if (self->enemy->deadflag != DEAD_NO)
		{
			hook->temp_think = hook->begin_wander;

			monkey_roar (self);

			//	pop off everything thru TASK_CHASE for this enemy
			ai_killed_enemy (self);

			return;
		}
		else if (dist > hook->attack_dist || !com->Visible (self, self->enemy))
		{
			ai_should_follow (self);
			hook->temp_think = self->think;

			if (rnd () < 0.3)
				monkey_roar (self);

			//	leaving, so pop off TASK_ATTACK
			task_pop (&hook->task_stack);

			return;
		}
		else
		{
			hook->begin_attack (self);

			return;
		}
	}

	ai_set_think (self, monkey_jump_attack, "monkey_jump_attack");
}

///////////////////////////
//	monkey_close_melee
//
///////////////////////////

void	monkey_close_melee (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	Vector			temp;
	float			dist;

	hook->dflags |= DFL_FACEENEMY;

	dist = qv_dist (self->s.origin, self->enemy->s.origin);

	// are we in a transition sequence?
	if (hook->frame_flags & FRAME_TRANS)
	{
		if (hook->frame_flags & FRAME_STATIC)
			ai_post_trans (self, FRAME_LOOP + FRAME_NEEDTRANS);
	}
	else
	{
		if (hook->frame_flags & FRAME_PLAYSOUND1)
		{
			hook->frame_flags = hook->frame_flags - FRAME_PLAYSOUND1;
					
			ai_fire_curWeapon (self, Vector (self->s.origin), Vector (self->enemy->s.origin), 0, 0);
		}
		
		if (hook->frame_flags & FRAME_PLAYSOUND2)
		{
			hook->frame_flags = hook->frame_flags - FRAME_PLAYSOUND2;
					
			ai_fire_curWeapon (self, Vector (self->s.origin), Vector (self->enemy->s.origin), 0, 0);
		}
		
		if (hook->frame_flags & FRAME_LAST)
		{
			hook->frame_flags -= FRAME_LAST;

			if (self->enemy->deadflag != DEAD_NO)
			{
				hook->temp_think = hook->begin_wander;

				//	killed the player, so roar
				monkey_roar (self);

				ai_killed_enemy (self);

				return;
			}
			else if (dist > hook->attack_dist || !com->Visible (self, self->enemy))
			{
				ai_should_follow (self);
				hook->temp_think = self->think;							

				//	player ran away, so roar sometimes
				//	always transition back to stand first
				if (rnd () < 0.3)
					monkey_roar (self);

				//	leaving, so pop off TASK_ATTACK
				task_pop (&hook->task_stack);

				return;
			}
			else
			{
				monkey_set_attack_seq (self);

				//	random chance to hop
				if (rnd () < 0.5)
					monkey_hop (self, 110);

				return;
			}
		}
	}

	ai_set_think (self, monkey_close_melee, "monkey_close_melee");
}

///////////////////////////
//	monkey_far_melee
//
///////////////////////////

void	monkey_far_melee (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	Vector			temp;
	float			dist;

/*	
	temp = self->goalentity->s.origin;
	com->CopyVector (self->goalentity->s.origin, self->enemy->s.origin);
	ai_face_goal (self);
	temp.set (self->goalentity->s.origin);
*/
	hook->dflags |= DFL_FACEENEMY;

	dist = qv_dist (self->s.origin, self->enemy->s.origin);

	if (hook->frame_flags & FRAME_PLAYSOUND2)
	{
		hook->frame_flags = hook->frame_flags - FRAME_PLAYSOUND2;
	
		ai_fire_curWeapon (self, Vector (self->s.origin), Vector (self->enemy->s.origin), 0, 0);
	}

	// attack without any transitions
	if (hook->frame_flags & FRAME_STATIC)
	{
		hook->frame_flags -= FRAME_STATIC;	

		if (self->enemy->deadflag != DEAD_NO)
		{
			hook->temp_think = hook->begin_wander;

			monkey_roar (self);

			//	pop off everything thru TASK_CHASE for this enemy
			ai_killed_enemy (self);

			return;
		}
		else if (dist > hook->attack_dist || !com->Visible (self, self->enemy))
		{
			ai_should_follow (self);
			hook->temp_think = self->think;

			if (rnd () < 0.3)
				monkey_roar (self);

			//	leaving, so pop off TASK_ATTACK
			task_pop (&hook->task_stack);

			return;
		}
		else
		{
			monkey_set_attack_seq (self);

			//	random chance to hop
			if (rnd () < 0.75)
			{
				monkey_hop (self, 70);
				ai_set_think (self, monkey_jump_attack, "monkey_far_melee");
			}

			return;
		}
	}

	ai_set_think (self, monkey_far_melee, "monkey_far_melee");
}

////////////////////////////
//	monkey_begin_attack
//
////////////////////////////

void	monkey_begin_attack (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	com->ZeroVector (self->velocity);

	hook->th_state = TH_ATTACK;
	hook->attack_finished = gstate->time + 1.0;

	monkey_set_attack_seq (self);
}

////////////////////////////
//	monkey_screw
//
//	screw with monkey's head
////////////////////////////

void	monkey_screw (userEntity_t *self, userEntity_t *other, userEntity_t *activator)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	playerHook_t	*mhook;
	doorHook_t		*dhook;
	userEntity_t	*head, *sw, *monkey;

	if (self->deadflag != DEAD_NO)
		return;

	monkey = sw = NULL;
	for (head = gstate->FirstEntity (); head; head = gstate->NextEntity (head))
	{
		if (!head->className)
			continue;

		//	find first monkey
		if (monkey == NULL && !stricmp (head->className, "monster_labmonkey"))
			monkey = head;

		if (!head->target)
			continue;

		if (!stricmp (head->target, self->targetname))
			sw = head;
	}

	if (!sw)
		return;

	dhook = (doorHook_t *) sw->userHook;

	if (hook->dflags & DFL_BERSERK)
	{
		hook->dflags -= DFL_BERSERK;
		hook->dflags |= DFL_EVIL;
	}
	else if (hook->dflags & DFL_EVIL)
	{
		hook->dflags -= DFL_EVIL;
		
		if (dhook->state == STATE_DOWN || dhook->state == STATE_BOTTOM)
			hook->dflags |= DFL_BERSERK;
		else
			hook->dflags |= DFL_GOOD;
	}
	else if (hook->dflags & DFL_GOOD)
	{
		hook->dflags -= DFL_GOOD;
		hook->dflags |= DFL_EVIL;
	}

	/////////////////////////////////////////////////////////////
	//	if good or evil, reset monkey's enemy
	/////////////////////////////////////////////////////////////

	if (hook->dflags & (DFL_GOOD + DFL_EVIL))
	{
		task_clear (&hook->task_stack);
		self->enemy = NULL;

		task_stand (self);
		task_wander (self);

		hook->begin_wander (self);
	}

	/////////////////////////////////////////////////////////////
	//	if good, target monsters
	/////////////////////////////////////////////////////////////

	if (hook->dflags & (DFL_GOOD))
		hook->find_target = ai_find_target_monster_bot;

	/////////////////////////////////////////////////////////////
	//	if evil, target clients and bots
	/////////////////////////////////////////////////////////////

	else if (hook->dflags & (DFL_EVIL))
		hook->find_target = ai_find_target_client_bot;

	/////////////////////////////////////////////////////////////
	//	if berserk, target everything
	/////////////////////////////////////////////////////////////

	else if (hook->dflags & (DFL_BERSERK))
		hook->find_target = ai_find_target_berserk;

	if (!monkey)
	{
		gstate->Con_Printf ("Monker-Meter registers no monkeys.\n");
		return;
	}

	//	print message if this is the first monkey
	if (monkey == self)
	{
		mhook = (playerHook_t *) monkey->userHook;

		if (mhook->dflags & DFL_GOOD)
			gstate->bprint ("Monkey-Meter set to GOOD MONKEY.\n");
		else if (mhook->dflags & DFL_EVIL)
			gstate->bprint ("Monkey-Meter set to BAD MONKEY.\n");
		else if (mhook->dflags & DFL_BERSERK)
			gstate->bprint ("Monkey-Meter set to APE-SHIT.\n");
	}
}

///////////////////////////////
//	monster_labmonkey
//
//	spawns a monkey during level load
///////////////////////////////

static	int		monkey_count = 0;

void	monster_labmonkey (userEntity_t *self)
{
	playerHook_t		*hook;
	int					i;

	hook = ai_init_monster (self, TYPE_LABMONKEY);

	self->className = "monster_labmonkey";
	self->netname = "lab monkey";
/*
	gstate->Precache_Sound ("monsters/monkey/monkeyattack1.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeyattack2.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeydeath1.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeydeath2.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeypain1.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeypain2.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeysight1.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeysight2.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeywander1.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeywander2.wav");
*/
	gstate->Precache_Sound ("monsters/monkey/monkeyscream1.wav");
	gstate->Precache_Sound ("monsters/monkey/monkeyattack1.wav");

	self->s.modelindex = gstate->ModelIndex ("models/labmonkey/labmonkey.md2");
	gstate->SetSize (self, -16.0, -16.0, -24.0, 16.0, 16.0, 32.0);

	////////////////////////////////////////////////////////////////
	// set up animation stuff
	////////////////////////////////////////////////////////////////

	ai_get_sequences (self);

	monkey_init_frametable (self);

	///////////////////////////////////////////////////////////
	// set up pointers to this creature's ai functions
	///////////////////////////////////////////////////////////

	//	E3Demo
//	hook->dflags -= DFL_EVIL;
//	hook->dflags |= DFL_GOOD;

	hook->find_target = ai_find_target_monster_bot;
	hook->begin_attack = monkey_begin_attack;

	hook->run_speed = 200;
	hook->walk_speed = 70;
	hook->attack_dist = 80;

	self->health = 100;
	hook->max_health = 100;
	hook->pain_chance = 5;
	hook->jump_chance = 50;
	self->mass = 1.5;
	self->gravity = 2.0;
	self->ang_speed [YAW] = 150;
	self->use = monkey_screw;

	self->think = ai_get_spawn_node;
	self->nextthink = gstate->time + 0.2;

	self->translucent = 0;

	//////////////////////////////////////////////
	//	give monkey its melee weapon
	//////////////////////////////////////////////
/*
	self->curWeapon = ai_weapon_create ("punch", melee_punch, ai_weapon_command);
	i = 10;
	ai_weapon_command (self->curWeapon, "base_damage", &i);
	ai_weapon_command (self->curWeapon, "rnd_damage", &i);
*/
	self->curWeapon = ai_init_weapon (self, 10, 10, 0, 0, 0, hook->attack_dist, Vector (0, 0, 0), "punch", melee_punch, WEAPON_TRACE);
}
