/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
--------------------------------------------------------------
The ACE Bot is a product of Steve Yeager, and is available from
the ACE Bot homepage, at http://www.axionfx.com/ace.

This program is a modification of the ACE Bot, and is therefore
in NO WAY supported by Steve Yeager.
*/

#include "../g_local.h"
#include "ai_local.h"

//ACE

//==========================================
// AI_CanMove
// Checks if bot can move (really just checking the ground)
// Also, this is not a real accurate check, but does a
// pretty good job and looks for lava/slime.  
//==========================================
qboolean AI_CanMove(edict_t *self, int direction)
{
	vec3_t forward, right;
	vec3_t offset,start,end;
	vec3_t angles;
	trace_t tr;

	// Now check to see if move will move us off an edge
	VectorCopy(self->s.angles,angles);

	if(direction == BOT_MOVE_LEFT)
		angles[1] += 90;
	else if(direction == BOT_MOVE_RIGHT)
		angles[1] -= 90;
	else if(direction == BOT_MOVE_BACK)
		angles[1] -=180;


	// Set up the vectors
	AngleVectors (angles, forward, right, NULL);

	VectorSet(offset, 36, 0, 24);
	G_ProjectSource (self->s.origin, offset, forward, right, start);

	VectorSet(offset, 36, 0, -100);
	G_ProjectSource (self->s.origin, offset, forward, right, end);

	trap_Trace(&tr, start, NULL, NULL, end, self, MASK_AISOLID);

	if(tr.fraction == 1.0 || tr.contents & (CONTENTS_LAVA|CONTENTS_SLIME))
	{
		//if(AIDevel.debugChased)	//jal: is too spammy. Temporary disabled
		//	G_PrintMsg (AIDevel.devguy, "%s: move blocked\n", self->bot.botStatus.netname);
		return qfalse;
	}

	return qtrue;// yup, can move
}


//===================
//  AI_IsStep
//  Checks the floor one step below the player. Used to detect
//  if the player is really falling or just walking down a stair.
//===================
qboolean AI_IsStep (edict_t *ent)
{
	vec3_t		point;
	trace_t		trace;
	
	//determine a point below
	point[0] = ent->s.origin[0];
	point[1] = ent->s.origin[1];
	point[2] = ent->s.origin[2] - (1.6*AI_STEPSIZE);
	
	//trace to point
	trap_Trace (&trace, ent->s.origin, ent->r.mins, ent->r.maxs, point, ent, MASK_PLAYERSOLID);
	//trace = gi.trace( ent->s.origin, ent->r.mins, ent->r.maxs, point, ent, MASK_PLAYERSOLID);
	
	if (trace.plane.normal[2] < 0.7 && !trace.startsolid)
		return qfalse;
	
	//found solid.
	return qtrue;
}


//==========================================
// AI_IsLadder
// check if entity is touching in front of a ladder
//==========================================
qboolean AI_IsLadder(vec3_t origin, vec3_t v_angle, vec3_t mins, vec3_t maxs, edict_t *passent)
{
	vec3_t	spot;
	vec3_t	flatforward, zforward;
	trace_t	trace;

	AngleVectors( v_angle, zforward, NULL, NULL);

	// check for ladder
	flatforward[0] = zforward[0];
	flatforward[1] = zforward[1];
	flatforward[2] = 0;
	VectorNormalize (flatforward);

	VectorMA ( origin, 1, flatforward, spot);

	trap_Trace(&trace, origin, mins, maxs, spot, passent, MASK_AISOLID);
	//trace = gi.trace( origin, mins, maxs, spot, passent, MASK_AISOLID);
	
	if ((trace.fraction < 1) && (trace.surfFlags & SURF_LADDER))
	//if ((trace.fraction < 1) && (trace.contents & CONTENTS_LADDER))
		return qtrue;

	return qfalse;
}


//==========================================
// AI_CheckEyes
// Helper for ACEMV_SpecialMove. 
// Tries to turn when in front of obstacle
//==========================================
qboolean AI_CheckEyes(edict_t *self, usercmd_t *ucmd)
{
	vec3_t  forward, right;
	vec3_t  leftstart, rightstart,focalpoint;
	vec3_t  dir,offset;
	trace_t traceRight;
	trace_t traceLeft;

	// Get current angle and set up "eyes"
	VectorCopy(self->s.angles,dir);
	AngleVectors (dir, forward, right, NULL);

	if(!self->movetarget)
		VectorSet(offset,200,0,self->r.maxs[2]*0.5); // focalpoint
	else
		VectorSet(offset,64,0,self->r.maxs[2]*0.5); // wander focalpoint 
	
	G_ProjectSource (self->s.origin, offset, forward, right, focalpoint);

	VectorSet(offset, 0, 18, self->r.maxs[2]*0.5);
	G_ProjectSource (self->s.origin, offset, forward, right, leftstart);
	offset[1] -= 36; //VectorSet(offset, 0, -18, self->r.maxs[2]*0.5);
	G_ProjectSource (self->s.origin, offset, forward, right, rightstart);

	trap_Trace(&traceRight, rightstart, NULL, NULL, focalpoint, self, MASK_AISOLID);
	trap_Trace(&traceLeft, leftstart, NULL, NULL, focalpoint, self, MASK_AISOLID);
	//traceRight = gi.trace( rightstart, NULL, NULL, focalpoint, self, MASK_AISOLID);
	//traceLeft = gi.trace( leftstart, NULL, NULL, focalpoint, self, MASK_AISOLID);

	// Find the side with more open space and turn
	if(traceRight.fraction != 1 || traceLeft.fraction != 1)
	{
		if(traceRight.fraction > traceLeft.fraction)
			self->s.angles[YAW] += (1.0 - traceLeft.fraction) * 45.0;
		else
			self->s.angles[YAW] += -(1.0 - traceRight.fraction) * 45.0;
		
		ucmd->forwardmove = 400;
		return qtrue;
	}
				
	return qfalse;
}

//==========================================
// AI_SpecialMove
// Handle special cases of crouch/jump
// If the move is resolved here, this function returns qtrue.
//==========================================
qboolean AI_SpecialMove(edict_t *self, usercmd_t *ucmd)
{
	vec3_t forward;
	trace_t tr;
	vec3_t	boxmins, boxmaxs, boxorigin;

	// Get current direction
	AngleVectors( tv(0, self->s.angles[YAW], 0), forward, NULL, NULL );

	//make sure we are bloqued
	VectorCopy( self->s.origin, boxorigin );
	VectorMA( boxorigin, 8, forward, boxorigin ); //move box by 8 to front
	trap_Trace( &tr, self->s.origin, self->r.mins, self->r.maxs, boxorigin, self, MASK_AISOLID);
	if( !tr.startsolid && tr.fraction == 1.0 ) // not bloqued
		return qfalse;

	//ramps //jabot092(2)
	if( tr.plane.normal[2] >= 0.7 )
		return qfalse;

	if( self->ai.pers.moveTypesMask & LINK_CROUCH || self->is_swim )
	{
		//crouch box
		VectorCopy( self->s.origin, boxorigin );
		VectorCopy( self->r.mins, boxmins );
		VectorCopy( self->r.maxs, boxmaxs );
		boxmaxs[2] = 14;	//crouched size
		VectorMA( boxorigin, 8, forward, boxorigin ); //move box by 8 to front
		//see if bloqued
		trap_Trace( &tr, boxorigin, boxmins, boxmaxs, boxorigin, self, MASK_AISOLID);
		if( !tr.startsolid ) // can move by crouching
		{
			ucmd->forwardmove = 400;
			ucmd->upmove = -400;
			return qtrue;
		}
	}

	if( self->ai.pers.moveTypesMask & LINK_JUMP && self->groundentity )
	{
		//jump box
		VectorCopy( self->s.origin, boxorigin );
		VectorCopy( self->r.mins, boxmins );
		VectorCopy( self->r.maxs, boxmaxs );
		VectorMA( boxorigin, 8, forward, boxorigin );	//move box by 8 to front
		//
		boxorigin[2] += ( boxmins[2] + AI_JUMPABLE_HEIGHT );	//put at bottom + jumpable height
		boxmaxs[2] = boxmaxs[2] - boxmins[2];	//total player box height in boxmaxs
		boxmins[2] = 0;
		//if( boxmaxs[2] > AI_JUMPABLE_HEIGHT ) //the box is smaller than AI_JUMPABLE_HEIGHT
		//{
			//boxmaxs[2] -= AI_JUMPABLE_HEIGHT;
			trap_Trace( &tr, boxorigin, boxmins, boxmaxs, boxorigin, self, MASK_AISOLID);
			if( !tr.startsolid )	//can move by jumping
			{
				ucmd->forwardmove = 400;
				ucmd->upmove = 400;
				
				return qtrue;
			}
		//}
	}

	//nothing worked, check for turning
	return AI_CheckEyes(self, ucmd);
}


//==========================================
// AI_ChangeAngle
// Make the change in angles a little more gradual, not so snappy
// Subtle, but noticeable.
// 
// Modified from the original id ChangeYaw code...
//==========================================
void AI_ChangeAngle (edict_t *ent)
{
	float	ideal_yaw;
	float   ideal_pitch;
	float	current_yaw;
	float   current_pitch;
	float	move;
	float	speed;
	vec3_t  ideal_angle;

	// Normalize the move angle first
	VectorNormalize(ent->ai.move_vector);

	current_yaw = anglemod(ent->s.angles[YAW]);
	current_pitch = anglemod(ent->s.angles[PITCH]);

	VecToAngles (ent->ai.move_vector, ideal_angle);

	ideal_yaw = anglemod(ideal_angle[YAW]);
	ideal_pitch = anglemod(ideal_angle[PITCH]);


	// Yaw
	if (current_yaw != ideal_yaw)
	{
		move = ideal_yaw - current_yaw;
		speed = ent->yaw_speed * FRAMETIME;
		if (ideal_yaw > current_yaw)
		{
			if (move >= 180)
				move = move - 360;
		}
		else
		{
			if (move <= -180)
				move = move + 360;
		}
		if (move > 0)
		{
			if (move > speed)
				move = speed;
		}
		else
		{
			if (move < -speed)
				move = -speed;
		}
		ent->s.angles[YAW] = anglemod (current_yaw + move);
	}


	// Pitch
	if (current_pitch != ideal_pitch)
	{
		move = ideal_pitch - current_pitch;
		speed = ent->yaw_speed * FRAMETIME;
		if (ideal_pitch > current_pitch)
		{
			if (move >= 180)
				move = move - 360;
		}
		else
		{
			if (move <= -180)
				move = move + 360;
		}
		if (move > 0)
		{
			if (move > speed)
				move = speed;
		}
		else
		{
			if (move < -speed)
				move = -speed;
		}
		ent->s.angles[PITCH] = anglemod (current_pitch + move);
	}
}



//==========================================
// AI_MoveToGoalEntity
// Set bot to move to it's movetarget. (following node path)
//==========================================
qboolean AI_MoveToGoalEntity(edict_t *self, usercmd_t *ucmd)
{
	if (!self->movetarget || !self->r.client)
		return qfalse;

	// If a rocket or grenade is around deal with it
	// Simple, but effective (could be rewritten to be more accurate)
	if(!Q_stricmp(self->movetarget->classname,"rocket") ||
	   !Q_stricmp(self->movetarget->classname,"grenade") ||
	   !Q_stricmp(self->movetarget->classname,"hgrenade"))
	{
		VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai.move_vector);
		AI_ChangeAngle(self);
		if(AIDevel.debugChased && bot_showcombat->integer)
			G_PrintMsg (AIDevel.chaseguy, "%s: Oh crap a rocket!\n",self->ai.pers.netname);

		// strafe left/right
		if(rand()%1 && AI_CanMove(self, BOT_MOVE_LEFT))
				ucmd->sidemove = -400;
		else if(AI_CanMove(self, BOT_MOVE_RIGHT))
				ucmd->sidemove = 400;
		return qtrue;

	}

	// Set bot's movement direction
	VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai.move_vector);
	AI_ChangeAngle(self);
	if(!AI_CanMove(self, BOT_MOVE_FORWARD) ) 
	{
		self->movetarget = NULL;
		ucmd->forwardmove = -100;
		return qfalse;
	}

	//Move
	ucmd->forwardmove = 400;
	return qtrue;
}


