// g_ai.c

#include "g_local.h"
#include "g_func.h"

qboolean AI_FindTarget (edict_t *self);

// RAFAEL	28-dec-98
qboolean AI_HearPlayer (edict_t *self);

extern cvar_t	*maxclients;

qboolean	enemy_vis;
qboolean	enemy_infront;
int			enemy_range;
float		enemy_yaw;

void AI_GetAvoidDirection( edict_t *self, edict_t *other );

//============================================================================



//============================================================================


/*
=============
ai_onfire_run

  Called while a character is running around on fire
==============
*/
void ai_onfire_run( edict_t *self, float dist )
{
	int side_result;

	if (self->onfiretime <= 0)  // ARME
	{	// stopping running around
		self->cast_info.currentmove = self->cast_info.move_stand;

		return;
	}

	if (!self->groundentity)
		return;

//	M_ChangeYaw( self );
	self->s.angles[YAW] = self->ideal_yaw;

	if (M_walkmove (self, self->s.angles[YAW], dist*0.5) && AI_SideTrace( self, 64, 0, 1 ))
		return;

	// look for a new direction

	side_result = AI_SideTrace( self, 128, 30, SIDE_RANDOM );

	if (side_result)
	{
		self->ideal_yaw = anglemod(self->s.angles[YAW] + (30.0 * side_result));
		return;
	}

	side_result = AI_SideTrace( self, 32, 45, SIDE_RANDOM );

	if (side_result)
	{
		self->ideal_yaw = anglemod(self->s.angles[YAW] + (30.0 * side_result));
		return;
	}

	// try a sharper angle
	side_result = AI_SideTrace( self, 64, 100, SIDE_RANDOM );

	if (side_result)
	{
		self->ideal_yaw = anglemod(self->s.angles[YAW] + (100.0 * side_result));
		return;
	}

	side_result = AI_SideTrace( self, 8, 150, SIDE_RANDOM );

	if (side_result)
	{
		self->ideal_yaw = anglemod(self->s.angles[YAW] + (150.0 * side_result));
		return;
	}

	// last ditch, try running to our attacker

//	ai_run( self, dist );

//	self->cast_info.currentmove = self->cast_info.move_stand;

};

/*
=============
ai_move

Move the specified distance at current facing.
This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward
==============
*/
void ai_move (edict_t *self, float dist)
{
	M_walkmove (self, self->s.angles[YAW], dist);

	// Ridah, added this so we turn to face our attacker after killed
	if (self->health <= 0)
	{
		M_ChangeYaw( self );

		if (self->velocity[2] < -50 && fabs(self->velocity[0]) < 50 && fabs(self->velocity[1]) < 50 && !self->groundentity && self->gravity < 1.0)
		{
			self->gravity = 0.1;	// sliding down wall?
		}
	}
}


/*
============
AI_CheckEvade
============
*/
void AI_CheckEvade( edict_t *self )
{
	vec3_t	vec;
	cast_memory_t *mem=NULL;
	edict_t	**enemy;

	if (self->enemy)
		enemy = &self->enemy;
	else
		enemy = &self->cast_info.avoid_ent;

	if (!(*enemy))
	{
		self->cast_info.currentmove = self->cast_info.move_stand;

		return;
	}

	if (self->combat_goalent)
	{
		if (	(*enemy)->client
			&&	(!(*enemy)->client->pers.holsteredweapon)
			&&	((*enemy)->client->pers.weapon))
		{	// get the fuck outta here
			self->cast_info.currentmove = self->cast_info.move_run;
			return;
		}
		else if (	!(*enemy)->client
				&&	((*enemy)->noise_time > (level.time - 2) && (*enemy)->noise_type == PNOISE_WEAPON))
		{	// get the fuck outta here
			self->cast_info.currentmove = self->cast_info.move_run;
			return;
		}
	}
	else if ((enemy == &self->cast_info.avoid_ent) || self->pain_debounce_time < (level.time - 3))
	{
		if ((*enemy) && (*enemy)->health > 0)
		{
			mem = level.global_cast_memory[self->character_index][(*enemy)->character_index];

			if (mem && (mem->timestamp > (level.time - 4)))
			{
				if ((*enemy)->client)
				{
					if (	(!(*enemy)->client->pers.holsteredweapon)
						&&	((*enemy)->client->pers.weapon))
					{
						float dist;

						VectorSubtract( (*enemy)->s.origin, self->s.origin, vec );
						dist = VectorNormalize( vec );
						
						self->ideal_yaw = vectoyaw( vec );

						 if (dist < AI_NOT_HOLSTERED_RANGE_1)
						{
							if (/*self->moral > MORAL_HAPPY ||*/ directly_infront((*enemy), self))
							{

								AI_StartAttack( self, (*enemy) );

								if (self->cast_info.sight)
									self->cast_info.sight ( self, (*enemy) );
							}
							// NEU else if (AI_SideTrace( self, -64, 0, 1) && dist < 64)
							//{	// go back again
							//	self->cast_info.currentmove = self->cast_info.move_evade;
							//}
						}
						else if (directly_infront((*enemy), self)
							&&	(*enemy)->client)
							//&&	(*enemy)->client->pers.weapon
						//	&&	(*enemy)->client->pers.weapon->ammo)
						{
							if (AI_CheckTakeCover( self ))
							{	// get outta here
								self->cast_info.currentmove = self->cast_info.move_run;
							}
							// NEU else if (AI_SideTrace( self, -64, 0, 1))
							//{	// go back again
							//	self->cast_info.currentmove = self->cast_info.move_evade;
							//}
							else //if (dist < 400)
							{
								AI_StartAttack( self, (*enemy) );
							}
						} 

						return;
					}
				}
				else	// AI character
				{
					// FIXME: do more thorough AI here?

					if (enemy == &self->enemy)
					{
						self->cast_info.currentmove = self->cast_info.move_run;
					}

					return;
				}
			}

			if (mem)
			{	// they're not hostile anymore
				mem->flags &= ~MEMORY_HOSTILE_ENEMY;
			}
		}

	}

	// noone to evade, resume standing

	(*enemy) = NULL;
	self->cast_info.currentmove = self->cast_info.move_stand;

}

/*
=============
AI_ClearSight

  Checks that we can draw a line to other, without hitting something else in the process
=============
*/
qboolean AI_ClearSight ( edict_t *self, edict_t *other, qboolean boxtrace )
{
	static vec3_t mins = { -8, -8,  -4 };
	static vec3_t maxs = {  8,  8,  4 };
	// Ridah, used for Flamethrower AI
	static vec3_t mins2 = { -12, -12, -12 };
	static vec3_t maxs2 = {  12,  12,  12 };
	vec3_t	start, end, vec;
	float	*tmins, *tmaxs;
	int		mask;
	trace_t tr;
	qboolean	rval;
	float	dist, progdist, distleft;

	if (!self || !other)
		return false;

	if (boxtrace)
	{
		if (self->cast_info.max_attack_distance == 384)
		{
			tmins = mins2;
			tmaxs = maxs2;
		}
		else
		{
			tmins = mins;
			tmaxs = maxs;
		}
		mask = MASK_PLAYERSOLID;
	}
	else
	{
		tmins = NULL;
		tmaxs = NULL;
		mask = MASK_SOLID;
	}

again:

	VectorCopy( self->s.origin, start );
	start[2] += self->viewheight-8*boxtrace;	// adjust for gun position
	VectorCopy( other->s.origin, end );
	end[2] += other->viewheight-8*boxtrace;

	VectorSubtract( end, start, vec );
	dist = VectorNormalize( vec );
	progdist = 0;
	distleft = dist;

	if (fabs(vec[2]) > 0.8)	// too steep
		return false;

	tr = gi.trace( start, tmins, tmaxs, end, self, mask );

	while (		(tr.fraction < 1)
			&&	(	((tr.ent) && (tr.ent->s.renderfx2 & RF2_SURF_ALPHA))
				 || (tr.contents & MASK_ALPHA)
				 ||	(tr.surface->flags & SURF_ALPHA)
// Rafael : this will allow immortals to see through non breaking glass				 
				 || (tr.contents & CONTENTS_TRANSLUCENT && tr.contents & CONTENTS_WINDOW && self->cast_info.aiflags & AI_IMMORTAL)
	 ))
	{
		progdist += tr.fraction * dist;
		distleft -= tr.fraction * dist;

		if (distleft < 14)
		{	// gone passed the point, return true
			return true;
		}

		VectorMA( tr.endpos, 12, vec, start );
		tr = gi.trace( start, tmins, tmaxs, end, self, mask );
	}

	rval = ((tr.ent == other) || (tr.fraction == 1));

	if (!rval && self->deadflag && self->viewheight < 32)
	{
		self->viewheight = 32;
		goto again;
	}

	return rval;
}


/*
=============
AI_MoveToPlatCenter

  moves the character towards the center position of the platform
=============
*/
void AI_MoveToPlatCenter( edict_t *self, edict_t *plat)
{
	vec3_t center, vec;
	node_t	*plat_node;
	
	if (plat->deadflag)
	{
		plat_node = level.node_data->nodes[plat->nav_data.cache_node];
		VectorCopy(plat_node->origin, center);
		center[2] = self->s.origin[2];

		VectorSubtract(center, self->s.origin, vec);
		VectorNormalize2(vec, vec);
		self->ideal_yaw = vectoyaw(vec);
		M_ChangeYaw(self);

		M_walkmove(self, self->ideal_yaw, 15);
	}
}

/*
=============
AI_CheckTalk

  look for a friend in close vicinity, to make a talking jesture at
=============
*/
qboolean AI_CheckTalk( edict_t *self )
{
	cast_memory_t *cast=NULL;
	float		best_dist=500, this_dist;
	edict_t		*best_cast=NULL;
	int			count=1, total;
	int			i;

	if (!self->cast_info.talk)	// can't speak
		return false;

	if (self->goal_ent || self->enemy)
		return false;

	// check our current talk_ent
	if (self->cast_info.talk_ent)
	{
		if (!(cast = level.global_cast_memory[self->character_index][self->cast_info.talk_ent->character_index]))
		{
			self->cast_info.talk_ent = NULL;
			return false;
		}

		if (	((cast->timestamp > (level.time - 1)) && (++count))
			&&	(/*!best_cast ||*/ infront(&g_edicts[cast->cast_ent], self))	// ignore them if they aren't facing us
			&&	((this_dist = VectorDistance(g_edicts[cast->cast_ent].s.origin, self->s.origin)) < best_dist))
		{
			best_cast = &g_edicts[cast->cast_ent];
			best_dist = this_dist;
		}
	}

	// check clients
	for (i=1; i<=maxclients->value; i++)
	{
		if (g_edicts[i].client && g_edicts[i].health > 0)
		{

			cast = level.global_cast_memory[self->character_index][g_edicts[i].character_index];

			if (cast)
			{
				if ((cast->timestamp_dist < 300) && (cast->timestamp > (level.time - 3))
					&&	(((&g_edicts[i] == self->leader) && (g_edicts[i].last_talk_time < (level.time - 4)))
					|| (directly_infront(&g_edicts[i], self))))
				{
					// pick the client that spoke most recently
					if (!best_cast || !best_cast->client || (best_cast->last_talk_time < g_edicts[i].last_talk_time))
					{
						best_cast = &g_edicts[i];
					}
				}
			}
		}
	}

	if (best_cast)
		goto done;

	if (self->cast_info.last_talk_turn > (level.time - 2))
	{	// ignore other AI characters for this time
		goto done;
	}


	if (!best_cast)		// only check for other ents if our current talk_ent isn't valid anymore
	for (i=0; i<3; i++)
	{
		switch (i)
		{
		case 0:
			cast = self->cast_info.friend_memory;
			break;
		case 1:
			cast = self->cast_info.neutral_memory;
			break;
		case 2:
			cast = self->cast_info.enemy_memory;
			break;
		}


		total = (int) (random() * 10);

		while (cast)
		{

			if (	((cast->timestamp > (level.time - 1)) && (++count))
				&&	(/*!best_cast ||*/ infront(&g_edicts[cast->cast_ent], self))	// ignore them if they aren't facing us
				&&	(g_edicts[cast->cast_ent].cast_info.aiflags & AI_TALK)
				&&	!(g_edicts[cast->cast_ent].cast_info.aiflags & AI_NO_TALK)
				&&	((this_dist = VectorDistance(g_edicts[cast->cast_ent].s.origin, self->s.origin)) < best_dist))
			{
				best_cast = &g_edicts[cast->cast_ent];
				best_dist = this_dist;
			}

//			if (count > 6)	// only check 5 visible friends at once
//				break;

			cast = cast->next;
		}

		if (i==0 && best_cast)
			break;
	}

	self->cast_info.last_talk_turn = level.time;

done:

	if (best_cast)
	{
		if (directly_infront(self, best_cast) || !self->cast_info.move_avoid_walk)
		{
			self->cast_info.talk(self);
		}
		// else	// turn to face them
		else 
		{
			vec3_t vec;

			if (self->cast_info.aiflags & AI_SIT_TALK)
			{
				self->cast_info.aiflags &= ~AI_SIT_TALK;
			}
			else
			{
				VectorSubtract(best_cast->s.origin, self->s.origin, vec);
				VectorNormalize(vec);
				self->ideal_yaw = vectoyaw(vec);
				self->cast_info.currentmove = self->cast_info.move_avoid_walk;
			}

			self->cast_info.aiflags |= AI_TALK;

			// talk straight away
			//self->last_talk_time = 0;
		}

		self->cast_info.talk_ent = best_cast;

		return true;
	}

	return false;
}

/*
=============
AI_TalkThink

  Generic talking for all characters (currently only supports males)
=============
*/
void AI_TalkThink( edict_t *self, qboolean ismale )
{
#include "voice_punk.h"
#include "voice_bitch.h"

	edict_t	*talk_ent;
	cast_memory_t	*mem;

	if (level.cut_scene_time)
		return;

	if (!(self->cast_info.aiflags & AI_TALK))
		return;

	if (self->enemy)	// don't talk normally if we're mad
		return;

	if (!(talk_ent = self->cast_info.talk_ent))
		return;

	if (VectorDistance( talk_ent->s.origin, self->s.origin ) > 600 || !directly_infront(talk_ent, self))
	{
		self->cast_info.talk_ent = NULL;
		return;
	}

	if (	(self->cast_info.talk_ent == &g_edicts[1])
		&&	(self->cast_info.talk_ent->last_talk_time < (level.time - TALK_OTHER_DELAY*2))
		&&	(self->last_talk_time > (level.time - TALK_SELF_DELAY)))
	{
		return;
	}

//	if (last_client_talk && last_client_talk > (level.time - TALK_OTHER_DELAY))
//		return;	// don't talk too much around the client

	if (self->leader && VectorDistance( self->s.origin, self->leader->s.origin ) < 384 )
	{
		if ((self->last_talk_time < (level.time - TALK_SELF_DELAY)) && (self->health < self->max_health/2))
		// if (self->last_talk_time < level.time - (TALK_SELF_DELAY * 4) && (self->health < 50))
		{	// we've been hired, only speak when we're hurt
			if (self->gender == GENDER_MALE)
			{
				Voice_Random( self, self->leader, friendlyhurt, NUM_FRIENDLYHURT );
				return;
			}
		}

		return;
	}
	

	if ((talk_ent->health <= 0) || !visible(self, talk_ent) || !infront(talk_ent, self))
	{
		self->cast_info.talk_ent = NULL;
		return;
	}

	mem = level.global_cast_memory[self->character_index][talk_ent->character_index];
	if (!mem || (mem->flags & MEMORY_NO_TALK))
		return;

	// say something!
	if (	(	(self->last_talk_time < (level.time - TALK_SELF_DELAY))			// we haven't spoken for a while
			 ||	(	(talk_ent->client || talk_ent->last_talk_time)		// if they haven't spoken yet, don't bother
				 &&	(talk_ent->last_talk_time > (level.time - TALK_OTHER_DELAY*1.5))	// or they've just said something, and we've allowed some time for them to finish saying it
				 &&	(talk_ent->cast_info.talk_ent == self)
				 &&	(self->last_talk_time < talk_ent->last_talk_time)
				 &&	(self->last_talk_time < (level.time - TALK_OTHER_DELAY))))
		&&	(talk_ent->last_talk_time < (level.time - TALK_OTHER_DELAY)))
	{
		if (talk_ent->client)
		{
			if (!self->cast_group)
			{	// we're neutral
				cast_memory_t	*mem;

				mem = level.global_cast_memory[self->character_index][talk_ent->character_index];

				if (!mem || !(mem->flags & MEMORY_ASSHOLE))
				{
					if (!EP_EventSpeech( self, talk_ent, say_neutral ))
					{
						if (ismale)
							Voice_Random(self, self->cast_info.talk_ent, neutral_talk, NUM_NEUTRAL_TALK);
						else
							Voice_Random(self, self->cast_info.talk_ent, f_neutral_talk, F_NUM_NEUTRAL_TALK);
					}
				}
				else	// he's a jerk
				{
					if (!EP_EventSpeech( self, talk_ent, say_hostile ))
					{
						if (ismale)
							Voice_Random(self, self->cast_info.talk_ent, neutral_asshole_talk, NUM_NEUTRAL_ASSHOLE_TALK);
						else
							Voice_Random(self, self->cast_info.talk_ent, f_profanity_level1, F_NUM_PROFANITY_LEVEL1);
					}
				}
			}
			else if (self->cast_group != talk_ent->cast_group)
			{	// profanity time

				if (!EP_EventSpeech( self, talk_ent, say_hostile ))
				{
					// JOSEPH 26-MAY-99
					// if first sighting, play specific sound
					if (self->name_index == NAME_NICKIBLANCO)
					{
						Voice_Specific(self, self->cast_info.talk_ent, nickiblanco, 10);							
					}
					else if (self->name_index == NAME_TYRONE)
					{
						Voice_Specific(self, self->cast_info.talk_ent, ty_tyrone, 10);							
					}	
					else if (self->name_index == NAME_MOKER)
					{
						Voice_Specific(self, self->cast_info.talk_ent, steeltown_moker, 10);							
					}								
					else if (self->name_index == NAME_JESUS)
					{
						Voice_Specific(self, self->cast_info.talk_ent, sr_jesus, 10);							
					}	
					else if (self->name_index == NAME_HEILMAN)
					{
						Voice_Specific(self, self->cast_info.talk_ent, heilman, 10);							
					}	
					else if (self->name_index == NAME_BLUNT)
					{
						Voice_Specific(self, self->cast_info.talk_ent, blunt, 10);							
					}	
					else if (self->name_index == NAME_KINGPIN)
					{
						Voice_Specific(self, self->cast_info.talk_ent, kingpin, 10);							
					}	
					else if (ismale && specific[0].last_played < 0.1)
					{
						Voice_Specific(self, self->cast_info.talk_ent, specific, 0); // What are you doing on my street
					}
					// END JOSEPH
					else if (!ismale && f_specific[0].last_played < 0.1)
					{
						Voice_Random (self, self->cast_info.talk_ent, f_specific, 4); // What are you doing on my street
					}

					// check profanity level
					else if (talk_ent->profanity_level <= 1)
					{
						if (ismale)
							Voice_Random(self, self->cast_info.talk_ent, profanity_level1, NUM_PROFANITY_LEVEL1);
						else
							Voice_Random(self, self->cast_info.talk_ent, f_profanity_level1, F_NUM_PROFANITY_LEVEL1);
					}
					else if (talk_ent->profanity_level <= 2)
					{
						if (ismale)
							Voice_Random(self, self->cast_info.talk_ent, profanity_level2, NUM_PROFANITY_LEVEL2);
						else
							Voice_Random(self, self->cast_info.talk_ent, f_profanity_level2, F_NUM_PROFANITY_LEVEL2);
					}
					else if (talk_ent->profanity_level == 3)
					{
						// Ridah, 31-may-99, fixed the game crashing everytime you cussed a character 3 times
						if (ismale)
							Voice_Random(self, self->cast_info.talk_ent, profanity_level3, NUM_PROFANITY_LEVEL3); // kill that mofo
						else
							Voice_Random(self, self->cast_info.talk_ent, f_profanity_level3, F_NUM_PROFANITY_LEVEL3); // kill that mofo

						// go for them!
						AI_MakeEnemy(self, self->cast_info.talk_ent, 0);

					}

					// allow more of a delay than usual
					last_client_talk = level.time + 3;

				}
			}
		}
		else	// AI -> AI chat
		{
			if (!EP_EventSpeech( self, talk_ent, say_neutral))
			{
				if (ismale)
					Voice_Random(self, self->cast_info.talk_ent, neutral_converse, NUM_NEUTRAL_CONVERSE);
				else
					Voice_Random(self, self->cast_info.talk_ent, f_neutral_converse, F_NUM_NEUTRAL_CONVERSE);
			}
		}

		if (self->last_talk_time == level.time)
		{
			if (!directly_infront( self, self->cast_info.talk_ent ))
			{
				self->cast_info.avoid( self, self->cast_info.talk_ent, true );
			}
			else if (self->cast_info.currentmove == self->cast_info.move_stand)
			{	// make talking gesture if we're just standing around
				self->cast_info.talk(self);

			}
		}
	}

}


#define	TAKECOVER_BEHIND			8
#define	TAKECOVER_INFRONT			13
#define	TAKECOVER_DIRECTLY_INFRONT	18

#define	SIGHT_ENEMY_ATTACKTIME		2.0	// attack for at least this duration before fleeing again

void CheckStillHiding( edict_t *self )
{

	if (self->owner->health <= 0)
	{
		G_FreeEdict( self );
		return;
	}

	if (!(self->owner->cast_info.aiflags & AI_TAKE_COVER) || (self->owner->cover_ent && !self->owner->cover_ent->inuse))
	{	// they're not hiding anymore, stop checking
		self->owner->combat_goalent = NULL;
		self->owner->cast_info.aiflags &= ~AI_TAKE_COVER;
		G_FreeEdict( self );
		return;
	}

	if ((self->owner->cover_ent != self->owner->enemy) && (self->owner->cover_ent->noise_time < (level.time - 5)))
	{	// stop hiding
		self->owner->combat_goalent = NULL;
		self->owner->cast_info.aiflags &= ~AI_TAKE_COVER;
		G_FreeEdict( self );
		return;
	}

	if (self->owner->cast_info.currentmove == self->owner->cast_info.move_evade)
	{
		// start running
		self->owner->cast_info.currentmove = self->owner->cast_info.move_run;
		self->nextthink = level.time + 1.0;
		return;
	}


	if (self->owner->combat_goalent == self && self->owner->cover_ent && ((VectorDistance(self->owner->s.origin, self->s.origin) > 256) || ValidBoxAtLoc( self->s.origin, self->owner->mins, tv(16, 16, 4), self->owner, MASK_PLAYERSOLID )))
	{
		float	dist;
		vec3_t vec;

		// turn to face them
		VectorSubtract( self->owner->cover_ent->s.origin, self->owner->s.origin, vec );
		vec[2] = 0;
		dist = VectorNormalize(vec);

		// should they keep hiding?
		if ((self->owner->cast_info.currentmove->frame->aifunc == ai_stand) && (VectorDistance( self->s.origin, self->owner->s.origin ) < 128))
		{
			self->owner->ideal_yaw = vectoyaw(vec);
			M_ChangeYaw( self->owner );

			// make sure we're still aware of them
			AI_RecordSighting(self->owner, self->owner->cover_ent, dist);

			if (AI_ClearSight( self->owner->cover_ent, self->owner, false ))
			{	// they can see us, we gotta do something or we're dead meat
				self->owner->combat_goalent = NULL;
				self->owner->cast_info.aiflags &= ~AI_TAKE_COVER;

				if (!(self->owner->cast_info.aiflags & AI_MELEE))
					self->owner->dont_takecover_time = 99999;//level.time + SIGHT_ENEMY_ATTACKTIME;	// allow some time to get some shots off

				self->owner->cast_info.aiflags &= ~AI_TAKECOVER_IGNOREHEALTH;

				G_FreeEdict( self );

				return;
			}

			if (self->owner->cast_info.aiflags & AI_TAKECOVER_IGNOREHEALTH)
			{	// stay here until they see us
				goto skiptest;
			}

			if (self->owner->cast_info.aiflags & AI_MELEE)
			{
				route_t route;

				// Ridah, modified this so hiding Melee guys don't foolishly keep running out into the line of fire for no reason
				if (	infront(self->owner->cover_ent, self->owner)		// we're infront of them
					&&	self->owner->cover_ent->client && self->owner->cover_ent->client->pers.weapon && self->owner->cover_ent->client->pers.weapon->ammo)
				{	// they have a weapon, keep hiding
					goto skiptest;
				}
				else if (VectorDistance( self->owner->cover_ent->s.origin, self->owner->s.origin ) > 256)
				{	// if too far away, keep hiding

					if (NAV_Route_EntityToEntity( self->owner, NULL, self->owner->cover_ent, VIS_PARTIAL, false, &route ))
					{	
						if (route.dist > 256)
							goto skiptest;
					}
					else	// no route, keep hiding
					{
						goto skiptest;
					}

				}
			}

			if (!infront( self->owner->cover_ent, self->owner ))
			{	// they're not looking at us
				if (self->owner->health > ((MORAL_MAX - self->owner->moral) * TAKECOVER_BEHIND))
				{
					self->owner->combat_goalent = NULL;
					self->owner->cast_info.aiflags &= ~AI_TAKE_COVER;
					self->owner->dont_takecover_time = 99999;//level.time + SIGHT_ENEMY_ATTACKTIME * (random()+1);
					G_FreeEdict( self );
					return;
				}
			}

			else if (self->owner->cast_group && !directly_infront( self->owner->cover_ent, self->owner ))
			{	// not looking directly at us, but in our general direction
				if (self->owner->health > ((MORAL_MAX - self->owner->moral) * TAKECOVER_INFRONT))
				{
					self->owner->combat_goalent = NULL;
					self->owner->cast_info.aiflags &= ~AI_TAKE_COVER;
					self->owner->dont_takecover_time = 99999;//level.time + SIGHT_ENEMY_ATTACKTIME;

					G_FreeEdict( self );

					return;
				}
			}

			else if (self->owner->cast_group)
			{	// looking directly at us
				if (self->owner->health > ((MORAL_MAX - self->owner->moral) * TAKECOVER_DIRECTLY_INFRONT))
				{
					self->owner->combat_goalent = NULL;
					self->owner->cast_info.aiflags &= ~AI_TAKE_COVER;
					self->owner->dont_takecover_time = 99999;//level.time + SIGHT_ENEMY_ATTACKTIME;

					G_FreeEdict( self );

					return;
				}
			}

		}

skiptest:

		if ((dist < 2000) && (VectorDistance( self->s.origin, self->owner->s.origin ) < 384) && (self->owner->last_getcombatpos < (level.time - 2.0)))
		{	// look for somewhere else to go to
			float *pos;

			if (	(self->owner->cast_info.aiflags & AI_MELEE)
				||	(self->owner->health < ((MORAL_MAX - self->owner->moral) * TAKECOVER_INFRONT)))
			{	// look for somewhere further away

				if (pos = NAV_GetHidePos( self->owner, self->owner->cover_ent, HIDEPOS_FURTHER ))
				{
					edict_t *combatent;

					if (VectorDistance( pos, self->s.origin ) > 96)
					{
						combatent = self;
						VectorCopy( pos, combatent->s.origin );
						
						self->owner->combat_goalent = combatent;
						combatent->cast_info.aiflags |= AI_GOAL_RUN;
						self->owner->cast_info.aiflags |= AI_TAKE_COVER;
						self->owner->cast_info.aiflags |= AI_RUN_LIKE_HELL;

						self->owner->cast_info.currentmove = self->owner->cast_info.move_run;
						self->owner->wait = -1;

						combatent->owner = self->owner;
						combatent->think = CheckStillHiding;
						combatent->nextthink = level.time + 1;	// give us some time to get there
					}

					return;
				}
			}

			else if (	(self->owner->moral > MORAL_HAPPY)
				&&	(self->owner->health > ((MORAL_MAX - self->owner->moral) * TAKECOVER_INFRONT)))
			{	// sneak up on them

				if (pos = NAV_GetHidePos( self->owner, self->owner->cover_ent, HIDEPOS_CLOSER ))
				{
					edict_t *combatent;

					if (VectorDistance( pos, self->s.origin ) > 96)
					{
						combatent = self;
						VectorCopy( pos, combatent->s.origin );
						
						self->owner->combat_goalent = combatent;
						combatent->cast_info.aiflags |= AI_GOAL_RUN;
						self->owner->cast_info.aiflags |= AI_TAKE_COVER;
						self->owner->cast_info.aiflags |= AI_RUN_LIKE_HELL;

						self->owner->cast_info.currentmove = self->owner->cast_info.move_run;
						self->owner->wait = -1;

						combatent->owner = self->owner;
						combatent->think = CheckStillHiding;
						combatent->nextthink = level.time + 1;	// give us some time to get there
					}

					return;
				}
			}

		}

		self->nextthink = level.time + 0.2;

		return;
	}

	if (self->owner->combat_goalent == self)
	{
		self->owner->combat_goalent = NULL;
		self->owner->cast_info.aiflags &= ~AI_TAKE_COVER;
	}

	// not hiding anymore

	G_FreeEdict( self );
}

/*
=============
AI_CheckTakeCover

  Returns true if we should go hide, and we've found a good hiding position
=============
*/
void Weapon_Blackjack (edict_t *ent);

qboolean AI_CheckTakeCover( edict_t *self )
{
	float *pos=NULL;
	qboolean	evade=true;		// make evade action before fleeing

	if (self->cast_info.aiflags & AI_NO_TAKE_COVER)
		return false;

	if (self->dont_takecover_time > level.time)
		return false;

	if (self->leader && !(self->cast_info.aiflags & AI_HOLD_POSITION))
		return false;	// no hiding when we're following

	if (self->combat_goalent)	// probably heading for a combat pos, let them continue
		return false;

	if (self->last_getcombatpos > (level.time - 1))
		return false;

	if (!self->enemy)
		return false;

	// Ridah 16-may-99, hired guys don't hide
	if (self->cast_group == 1 && self->leader)
		return false;
	// done.

	// should we hide?

	// Ridah, always hide every now and then if we're a firing enemy, since that's what any sane human
	//	being would do
	if (	!(self->cast_info.aiflags & AI_MELEE)
//		&&	((rand()%MORAL_MAX) < (MORAL_MAX+2 - self->moral))
		&&	!(self->enemy->cast_info.aiflags & AI_MELEE)
		&&	(!self->enemy->client || (self->enemy->client->pers.weapon && self->enemy->client->pers.weapon->ammo))
		&&	/*directly_*/infront(self->enemy, self)
		&&	AI_ClearSight( self->enemy, self, false ))
//		&&	(self->last_gethidepos < (level.time - (1.0 + self->moral/MORAL_MAX))))
	{
//		cast_memory_t	*mem;
//		edict_t			*trav;
		qboolean		nohide=false;
/*
		// TEAM AI: if we have a friend around, only take cover if they're happily firing
		mem = self->cast_info.friend_memory;
		while (mem)
		{
			trav = &g_edicts[mem->cast_ent];

			if (	(mem->timestamp > (level.time - 10))
				&&	(trav->health > 0)
				&&	(trav->enemy)
				&&	(!(trav->cast_info.aiflags & AI_MELEE))
				&&	(	(trav->noise_time < (level.time - 2))
					 ||	(trav->cast_info.aiflags & AI_TAKE_COVER)))
			{
				nohide = true;
				break;
			}

			mem = mem->next;
		}
*/
		if (!nohide && (pos = NAV_GetHidePos( self, self->enemy, HIDEPOS_ANY )))
		{
			evade = false;
			goto done;
		}
	}

	if (	(self->cast_info.aiflags & AI_MELEE)
		&&	(	(self->enemy->client)
			 &&	(self->enemy->client->pers.weapon)
			 &&	(self->enemy->client->pers.weapon->ammo))
		&&	(random() < 0.3 || VectorDistance( self->enemy->s.origin, self->s.origin ) > 128))
	{	// don't do any other tests, we REALLY want to hide from this enemy!

		if (pos = NAV_GetHidePos( self, self->enemy, HIDEPOS_FURTHER ))
		{
			goto done;
		}

	}
	else if (infront( self->enemy, self ))
	{
		if (directly_infront( self->enemy, self ))
		{
			if (self->health > ((MORAL_MAX - self->moral) * TAKECOVER_DIRECTLY_INFRONT))
				return false;
		}
		else
		{
			if (self->health > ((MORAL_MAX - self->moral) * TAKECOVER_INFRONT))
				return false;
		}
	}
	else if (self->health > ((MORAL_MAX - self->moral) * TAKECOVER_BEHIND))
		return false;


	// find somewhere to go
	if (!pos)
	{

		if (self->cast_group)
		{	// look for a friend with a gun (assume we know where all our friends are)
			int i;
			edict_t *other, *best=NULL;
			float	best_dist=2048, this_dist;
			route_t	route;

			for (i=0; i<level.num_characters; i++)
			{
				other = level.characters[i];

				if (!other)
					continue;

				if (other->health <= 0 || !other->inuse)
					continue;

				if (self->cast_group != other->cast_group)
					continue;

				if (self == other)
					continue;

				this_dist = VectorDistance( self->s.origin, other->s.origin );

				if ((this_dist < 384) && other->enemy)
					continue;

				if (other->client)
					goto accepted;

				if (best)
				{
					if (best->cast_info.aiflags & AI_MELEE)
					{
						if (!(other->cast_info.aiflags & AI_MELEE))
						{
							goto accepted;
						}
					}
					else
					{
						if (other->cast_info.aiflags & AI_MELEE)
						{	// no good
							continue;
						}
					}
				}
				else if (!(self->cast_info.aiflags & AI_MELEE))
				{
					if (other->cast_info.aiflags & AI_MELEE)
					{	// no good, we have weapon and this friend doesn't
						continue;
					}
					else	// is it worth going to get them?
					{
						if (!NAV_Route_EntityToEntity( self, NULL, other, VIS_PARTIAL, false, &route ))
							continue;

						if (route.dist > 1000)	// too far away
							continue;
					}
				}

				if (this_dist > best_dist)
					continue;

accepted:

				best = other;
				best_dist = this_dist;

				if (other->client)
					break;

			}

			if (best)
			{
//gi.dprintf( "%s fleeing to %s\n", self->name, best->name );
				pos = best->s.origin;
				self->cover_ent = self->enemy;
				self->last_getcombatpos = level.time;
				self->last_gethidepos = level.time;
				goto done;
			}
		}

		pos = NAV_GetHidePos( self, self->enemy, HIDEPOS_FURTHER );
	}

done:

	if (pos)
	{
		edict_t *combatent;

		combatent = G_Spawn();
		VectorCopy( pos, combatent->s.origin );
		
		self->combat_goalent = combatent;
		combatent->cast_info.aiflags |= AI_GOAL_RUN;
		self->cast_info.aiflags |= AI_TAKE_COVER;
		self->cast_info.aiflags |= AI_RUN_LIKE_HELL;
		self->wait = -1;

		combatent->owner = self;
		combatent->think = CheckStillHiding;
		combatent->nextthink = level.time + 1;	// give us some time to get there
/*
		// make sure we run AWAY from our enemy
		{
			node_t *node;

			if (node = NAV_GetClosestNode( self, VIS_PARTIAL, true, true ))
				self->nav_data.goal_index = node->index + 1;
		}
*/
	}

	/* NEU if (	evade
		&&	(self->last_stand_evade < (level.time - 4))
		&&	(pos || ((rand()%10) < 3))
		&&	(self->moral < 3)
		&&	self->cast_info.move_evade
		&&	self->maxs[2] == self->cast_info.standing_max_z
		&&	directly_infront( self->enemy, self )
		&&	AI_ClearSight(self, self->enemy, false)
		&&	self->health > self->max_health/2)
	{
		self->last_stand_evade = level.time;
		self->cast_info.currentmove = self->cast_info.move_evade;
		if (self->cast_info.backoff)
			self->cast_info.backoff( self, self->enemy );
	} */

	return (pos != NULL);
}

/*
=============
AI_ForceTakeCover
=============
*/
qboolean AI_ForceTakeCover( edict_t *self, edict_t *enemy, qboolean ignorehealth )
{
	float *pos;
	int		hidepos_type;

	if (self->cast_info.aiflags & AI_NO_TAKE_COVER)
		return false;

	// Note to Ryan: take a look at this. nasty hack because of crash bug
	if (!(enemy))
		return false;

	if (enemy->svflags & SVF_MONSTER)
		hidepos_type = HIDEPOS_FURTHER;
	else
		hidepos_type = HIDEPOS_ANY;

	if (pos = NAV_GetHidePos( self, enemy, hidepos_type ))
	{
		edict_t *combatent;

		combatent = G_Spawn();
		VectorCopy( pos, combatent->s.origin );
		
		self->combat_goalent = combatent;
		combatent->cast_info.aiflags |= AI_GOAL_RUN;
		combatent->cast_info.aiflags |= AI_RUN_LIKE_HELL;
		self->cast_info.aiflags |= AI_TAKE_COVER;
		self->cast_info.aiflags |= AI_RUN_LIKE_HELL;

		combatent->owner = self;
		combatent->think = CheckStillHiding;
		combatent->nextthink = level.time + 1;	// give us some time to get there

		if (ignorehealth)
			self->cast_info.aiflags |= AI_TAKECOVER_IGNOREHEALTH;

		if (self->maxs[2] == self->cast_info.standing_max_z)
			self->cast_info.currentmove = self->cast_info.move_run;
/*
		// make sure we run AWAY from our enemy
		{
			node_t *node;

			if (node = NAV_GetClosestNode( self, VIS_PARTIAL, true, true ))
				self->nav_data.goal_index = node->index + 1;
		}
*/
		return true;
	}

	return false;
}

/*
=============
AI_TooClose

  returns true if "goal" is within the AI_TOO_CLOSE_DIST range from "self"
  mostly used to make sure followers don't obstruct their leader
=============
*/
qboolean AI_TooClose(edict_t *self, edict_t *goal)
{
	float	scale;

	if (VectorCompare(goal->velocity, vec3_origin))
		return false;

	// allow to walk real close if they're standing on a plat and not moving
	if (goal->groundentity && goal->groundentity->use && VectorCompare(goal->groundentity->velocity, vec3_origin))
		return false;

	scale = 1.0;
	if (VectorLength(goal->velocity))
		scale = 1.5;

	return (VectorDistance(self->s.origin, goal->s.origin) < AI_TOO_CLOSE_DIST*scale);
}

/*
=============
AI_FollowLeader

  modification of AI_TooClose(), returns true if we need to move away from, or towards the goal
=============
*/
qboolean AI_FollowLeader(edict_t *self, edict_t *goal)
{
	float dist;
	float	scale;

	dist = VectorDistance(self->s.origin, goal->s.origin);

	// allow to walk real close if they're standing on a plat and not moving
	if (	goal->groundentity
		&&	goal->groundentity->use)
	{
		if (goal->groundentity != self->groundentity)
			return true;
		else if (dist < 64 && !VectorCompare(goal->velocity, vec3_origin))
			return true;
		else
			return false;
	}

	scale = 1.0;
	if (VectorLength(goal->velocity))
		scale = 1.5;

	return ((dist < AI_TOO_CLOSE_DIST*scale) || (dist > scale*(AI_GUARDING_DIST * (infront(goal, self) ? 1 : 0.5))));
}

/*
==============
AI_End_CrouchStand_Down

  Goes to the standing animation according to what we're currently doing
==============
*/
void AI_End_CrouchStand_Down(edict_t *self)
{
	self->cast_info.currentmove = self->cast_info.move_crwalk;
}

/*
==============
AI_End_CrouchStand_Up

  Goes to the standing animation according to what we're currently doing
==============
*/
void AI_End_CrouchStand_Up(edict_t *self)
{
	self->cast_info.currentmove = self->cast_info.move_run;
}

/*
==============
AI_EndAttack

  resume attacking if they so desire, otherwise "stand"

  usually called at the end of pain, or attacking animations
==============
*/
void AI_EndAttack(edict_t *self)
{
	mmove_t *oldmove;

	// hack to turn off the flamethrow effect
	if (self->s.renderfx2 & RF2_FLAMETHROWER)
		self->s.renderfx2 &= ~RF2_FLAMETHROWER;
	
	if (self->enemy && AI_CheckTakeCover(self))
	{
		if (self->cast_info.currentmove != self->cast_info.move_evade)
			self->cast_info.currentmove = self->cast_info.move_run;
		return;
	}

	oldmove = self->cast_info.currentmove;
	if (!self->enemy || !self->cast_info.checkattack(self))
	{
		self->cast_info.currentmove = self->cast_info.move_stand;
		self->cast_info.talk(self);

		if (self->enemy)
			self->cast_info.currentmove = self->cast_info.move_run;
		
	}
}

/*
==============
AI_GetOrientation

  returns one of ORIENTATION_* depending on which side of "self", "other" is on
==============
*/
int AI_GetOrientation( edict_t *self, edict_t *other )
{
	vec3_t	right, rorg, lorg, rvec, lvec;
	float diff;

	// determine right or left side impact
	AngleVectors(self->s.angles, NULL, right, NULL);
	VectorMA(self->s.origin,  16, right, rorg);
	VectorMA(self->s.origin, -16, right, lorg);

	VectorSubtract(other->s.origin, rorg, rvec);
	VectorSubtract(other->s.origin, lorg, lvec);

	diff = VectorLength(rvec) - VectorLength(lvec);

	if (diff < -8) // right
	{
		return ORIENTATION_RIGHT;
	}
	else if (diff > 8) // left
	{
		return ORIENTATION_LEFT;
	}

	// center
	return ORIENTATION_CENTER;
}

/*
==============
AI_CheckStillInair

  called while jumping, so we stay on a single jumping frame while in the air
==============
*/
void AI_CheckStillInair(edict_t *self)
{
	if (!self->groundentity)
		self->s.frame--;		// stay on the current frame
	else
	{
		float dist;

		dist = self->fall_height - self->s.origin[2];

		if ( dist < 48)
		{	// get out of this anim
			self->cast_info.currentmove->endfunc(self);
		}
		else if ( dist > 256 )
		{
			T_Damage( self, world, world, vec3_origin, self->s.origin, vec3_origin, 2 + (int)((0.01*dist)*(0.01*dist)), 0, 0, MOD_FALLING );
		}
	}
}

/*
==============
AI_EndJump

  called at the end of a jump, so we return to running, or crouch walking frames
==============
*/
void AI_EndJump(edict_t *self)
{
	if (!self->cast_info.move_run)
		return;

	// clear cached nodes
//	self->nav_data.cache_node = -1;
//	self->nav_data.goal_index = 0;

	// resume walking
	if (self->maxs[2] < self->cast_info.standing_max_z)
		self->cast_info.currentmove = self->cast_info.move_crwalk;
	else
		self->cast_info.currentmove = self->cast_info.move_run;
}


// JOSEPH 20-NOV-98
/*
==============
AI_AfterLife

  Generic character elimination routine.
==============
*/
void AI_AfterLife(edict_t *self)
{
	// Seconds dead
	self->deadticks++;

	// Set body on floor angle
	if (self->deadticks <= 1)
	{
		/*vec3_t	avec, stop;
		trace_t	trace;
			
		VectorCopy(self->s.origin, stop);
		stop[2] -= 16*2;

		trace = gi.trace (self->s.origin, self->mins, self->maxs, stop, self, MASK_DEADSOLID);
		if (trace.fraction < 1)
		{
			VectorCopy (trace.plane.normal, avec);
			avec[0] = acos(trace.plane.normal[2])/M_PI*180;
			avec[2] = atan2(trace.plane.normal[1], trace.plane.normal[0])/M_PI*180;
			avec[1] = self->s.angles[1];
			
			if (avec[2] == 180)
			{
				avec[0] = -avec[0];
				avec[2] = 0; 
			}
			else if (avec[2] == 90)
			{
				avec[2] = avec[0];
				avec[0] = 0; 
			}
			else if (avec[2] == -90)
			{
				avec[2] = -avec[0];
				avec[0] = 0; 
			}

			VectorCopy (avec, self->s.angles);

			// Ridah, the angles seem to be opposite?
			VectorScale( self->s.angles, -1, self->s.angles );
			self->s.angles[YAW] = -self->s.angles[YAW];	// return YAW to normal
		}*/
		
		/*vec3_t	avec1, avec2, avect, angles, start, stop, headorg, legsorg;
		trace_t	trace;
		
		// Use object_bounds to get head position
		{
			vec3_t org, ang, mins, maxs;
			vec3_t forward, right, up;
			vec3_t rmins, rmaxs, pmins, pmaxs;

			VectorCopy (self->s.origin, org);
			VectorCopy (self->s.angles, ang);
			
			VectorCopy (self->s.model_parts[PART_HEAD].object_bounds[0][self->s.frame].mins, mins);
			VectorCopy (self->s.model_parts[PART_HEAD].object_bounds[0][self->s.frame].maxs, maxs);
			
			AngleVectors (ang, forward, right, up);

			VectorMA (org, ((mins[0] + maxs[0]) * 0.5), forward, org);
			VectorMA (org, -((mins[1] + maxs[1]) * 0.5), right, org);
			VectorMA (org, (mins[2] + maxs[2]) * 0.5, up, org);

			// find rotated positions of mins/maxs, and then build the new min/max
			VectorScale ( forward, mins[0], rmins);
			VectorMA (rmins, -mins[1], right, rmins);
			VectorMA (rmins, mins[2], up, rmins);

			VectorScale ( forward, maxs[0], rmaxs);
			VectorMA (rmaxs, -maxs[1], right, rmaxs);
			VectorMA (rmaxs, maxs[2], up, rmaxs);

			pmins[0] = (rmins[0] < rmaxs[0] ? rmins[0] : rmaxs[0]);
			pmins[1] = (rmins[1] < rmaxs[1] ? rmins[1] : rmaxs[1]);
			pmins[2] = (rmins[2] < rmaxs[2] ? rmins[2] : rmaxs[2]);

			pmaxs[0] = (rmins[0] > rmaxs[0] ? rmins[0] : rmaxs[0]);
			pmaxs[1] = (rmins[1] > rmaxs[1] ? rmins[1] : rmaxs[1]);
			pmaxs[2] = (rmins[2] > rmaxs[2] ? rmins[2] : rmaxs[2]);

			// now align the mins/maxs with the origin
			mins[0] = pmins[0] - (0.5*(pmaxs[0] + pmins[0]));
			mins[1] = pmins[1] - (0.5*(pmaxs[1] + pmins[1]));
			mins[2] = pmins[2] - (0.5*(pmaxs[2] + pmins[2]));
			maxs[0] = pmaxs[0] - (0.5*(pmaxs[0] + pmins[0]));
			maxs[1] = pmaxs[1] - (0.5*(pmaxs[1] + pmins[1]));
			maxs[2] = pmaxs[2] - (0.5*(pmaxs[2] + pmins[2]));

			headorg[0] = org[0] + (mins[0] + maxs[0]) / 2;
			headorg[1] = org[1] + (mins[1] + maxs[1]) / 2;
			headorg[2] = org[2] + (mins[2] + maxs[2]) / 2;
		}
		
		// Use object_bounds to get legs position
		{
			vec3_t org, ang, mins, maxs;
			vec3_t forward, right, up;
			vec3_t rmins, rmaxs, pmins, pmaxs;

			VectorCopy (self->s.origin, org);
			VectorCopy (self->s.angles, ang);
			
			VectorCopy (self->s.model_parts[PART_LEGS].object_bounds[0][self->s.frame].mins, mins);
			VectorCopy (self->s.model_parts[PART_LEGS].object_bounds[0][self->s.frame].maxs, maxs);
			
			AngleVectors (ang, forward, right, up);

			VectorMA (org, ((mins[0] + maxs[0]) * 0.5), forward, org);
			VectorMA (org, -((mins[1] + maxs[1]) * 0.5), right, org);
			VectorMA (org, (mins[2] + maxs[2]) * 0.5, up, org);

			// find rotated positions of mins/maxs, and then build the new min/max
			VectorScale ( forward, mins[0], rmins);
			VectorMA (rmins, -mins[1], right, rmins);
			VectorMA (rmins, mins[2], up, rmins);

			VectorScale ( forward, maxs[0], rmaxs);
			VectorMA (rmaxs, -maxs[1], right, rmaxs);
			VectorMA (rmaxs, maxs[2], up, rmaxs);

			pmins[0] = (rmins[0] < rmaxs[0] ? rmins[0] : rmaxs[0]);
			pmins[1] = (rmins[1] < rmaxs[1] ? rmins[1] : rmaxs[1]);
			pmins[2] = (rmins[2] < rmaxs[2] ? rmins[2] : rmaxs[2]);

			pmaxs[0] = (rmins[0] > rmaxs[0] ? rmins[0] : rmaxs[0]);
			pmaxs[1] = (rmins[1] > rmaxs[1] ? rmins[1] : rmaxs[1]);
			pmaxs[2] = (rmins[2] > rmaxs[2] ? rmins[2] : rmaxs[2]);

			// now align the mins/maxs with the origin
			mins[0] = pmins[0] - (0.5*(pmaxs[0] + pmins[0]));
			mins[1] = pmins[1] - (0.5*(pmaxs[1] + pmins[1]));
			mins[2] = pmins[2] - (0.5*(pmaxs[2] + pmins[2]));
			maxs[0] = pmaxs[0] - (0.5*(pmaxs[0] + pmins[0]));
			maxs[1] = pmaxs[1] - (0.5*(pmaxs[1] + pmins[1]));
			maxs[2] = pmaxs[2] - (0.5*(pmaxs[2] + pmins[2]));

			legsorg[0] = org[0] + (mins[0] + maxs[0]) / 2;
			legsorg[1] = org[1] + (mins[1] + maxs[1]) / 2;
			legsorg[2] = org[2] + (mins[2] + maxs[2]) / 2;
		}

		VectorCopy(headorg, start);
		VectorCopy(headorg, stop);
		stop[2] = start[2] - 16*4;
		trace = gi.trace (start, NULL, NULL, stop, self, MASK_DEADSOLID);

		if (trace.fraction < 1)
		{
			VectorCopy(trace.endpos, avec1);
		}
		else
		{
			VectorCopy(start, avec1);
		}

		VectorCopy(legsorg, start);
		VectorCopy(legsorg, stop);
		stop[2] = start[2] - 16*4;
		trace = gi.trace (start, NULL, NULL, stop, self, MASK_DEADSOLID);

		if (trace.fraction < 1)
		{
			VectorCopy(trace.endpos, avec2);
		}
		else
		{
			VectorCopy(start, avec2);
		}

		if ((avec1[2] != avec2[2]))
		{
			if ((headorg[0] > self->s.origin[0]) || (headorg[1] > self->s.origin[1]))
			{
				VectorSubtract(avec2, avec1, avect);
			}
			else
			{
				VectorSubtract(avec1, avec2, avect);
			}
			
			VectorNormalize(avect);
			vectoangles(avect, angles);	
			angles[1] = self->s.angles[1];
			VectorCopy (angles, self->s.angles);
		}*/
	}
		
	// If the body is not to be moved 
	if (!self->deadticks)
	{
		self->think = NULL;
		self->nextthink = -1;
		self->solid = 0;
		return;		
	}
	
	if (self->deadticks > (60*1))
	{
		edict_t	*other=NULL;
		trace_t tr;
		vec3_t	end, travelvec;
		byte	i;
		long	len;
		float	dir;
		int     dir2;
		byte    BW = SFX_BLOOD_WIDTH;
		byte    BH = SFX_BLOOD_HEIGHT;
		long    rnd1, rnd2;

		// Ridah, 11/22/98 added distance check and optimization

		// If no player can see the body
//		while (other = G_Find(other, FOFS(classname), "player"))
		for (i=0; i<(int)maxclients->value; i++)
		{
			other = &g_edicts[1 + i];	// clients start at g_edicts[1]

			if (!other->inuse)
				continue;

			if (infront(other, self) || (VectorDistance(other->s.origin, self->s.origin) < 1024))
			{	
				self->nextthink = level.time + FRAMETIME * 20;
				return;
			}
		}

		// Determine drag direction
		dir = -10;
		dir2 = 0;
		VectorCopy (self->s.origin, end);
		end[dir2] += dir * 20;
		tr = gi.trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT);
		VectorSubtract(self->s.origin, tr.endpos, travelvec);
		len = VectorNormalize(travelvec);			
		if (len > 100) goto dragit;

		dir = 10;
		dir2 = 0;
		VectorCopy (self->s.origin, end);
		end[dir2] += dir * 20;
		tr = gi.trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT);
		VectorSubtract(self->s.origin, tr.endpos, travelvec);
		len = VectorNormalize(travelvec);			
		if (len > 100) goto dragit;

		dir = -10;
		dir2 = 1;
		VectorCopy (self->s.origin, end);
		end[dir2] += dir * 20;
		tr = gi.trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT);
		VectorSubtract(self->s.origin, tr.endpos, travelvec);
		len = VectorNormalize(travelvec);			
		if (len > 100) goto dragit;

		dir = 10;
		dir2 = 1;
		VectorCopy (self->s.origin, end);
		end[dir2] += dir * 20;
		tr = gi.trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT);
		VectorSubtract(self->s.origin, tr.endpos, travelvec);
		len = VectorNormalize(travelvec);			
		if (len > 100) goto dragit; else goto forgetit;

	dragit:

		rnd1 = ((rand()&7)-4);
		
		// Drag the body
		for (i=0; i <= 10; i++)
		{
			VectorCopy (self->s.origin, end);
			end[2] -= 32+8;

			tr = gi.trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT);

			SurfaceSpriteEffect(SFX_SPRITE_SURF_BLOOD1, BW--, BH--,
							tr.ent, tr.endpos, tr.plane.normal);
			
			self->s.origin[dir2] += dir;

			rnd2 = ((rand()&3)-2);

			if (dir == 1)
				self->s.origin[2] += (rnd1 + rnd2); 	
			else
				self->s.origin[1] += (rnd2 + rnd2); 	
		}

	forgetit:

		// Unload this cast's memory, since we won't be needing it anymore
		//AI_UnloadCastMemory( self );

		// Remove the body
		self->think = NULL;
		self->nextthink = -1;
		self->solid = 0;
		self->svflags |= SVF_NOCLIENT;
		return;
	}

	self->nextthink = level.time + FRAMETIME * 10;
}
// END JOSEPH


/*	trace_t tr;
	vec3_t	end;
	
	VectorCopy (self->s.origin, end);
	end[2] -= 32+8;

	tr = gi.trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT);

	SurfaceSpriteEffect(SFX_SPRITE_SURF_BLOOD1, SFX_BLOOD_WIDTH, SFX_BLOOD_HEIGHT,
						tr.ent, tr.endpos, tr.plane.normal);
*/
			
/*
==============
AI_EndDeath

  Generic end of death routine. Should call this for all characters that die, then do any
  special stuff for that character.
==============
*/
void AI_EndDeath(edict_t *self)
{
	// Ridah 16-may-99, stop dead bodies from sinking through floor
	if (!self->groundentity)
	{
		return;  // inferno
	}
	// done.

	// Ridah, 7-5-99, fixes shooting dead bodies
//	if (!(self->svflags & SVF_DEADMONSTER))
	if (self->mins[0] != -64)
	{
		// Ridah, had to expand this even further to accomodate the punk death frames
		VectorSet (self->mins, -64, -64, -24);
		VectorSet (self->maxs,  64,  64, -4);
		self->movetype = MOVETYPE_NONE;
		self->svflags |= SVF_DEADMONSTER;
		gi.linkentity (self);
/*
		// if this position isn't valid, use the old, smaller box
		{
			trace_t tr;

			tr = gi.trace( self->s.origin, self->mins, self->maxs, self->s.origin, self, MASK_SHOT );

			if (tr.startsolid)
			{
				VectorSet (self->mins, -16, -16, -24);
				VectorSet (self->maxs,  16,  16, 0);
				gi.linkentity (self);
			}
		}
*/
	}

	// Ridah, modified this so we only spawn blood once
	if (!(self->flags & FL_SPAWNED_BLOODPOOL) && VectorCompare( self->velocity, vec3_origin ))
	{
		SpawnBloodPool (self);
		self->flags |= FL_SPAWNED_BLOODPOOL;
	} 

	//self->think = Body_Animate; 
	   //     self->nextthink = level.time + 0.8;
	AI_AfterLife(self);


// inferno
		//self->solid = 0;
		//self->svflags |= SVF_NOCLIENT;
		//self->think = gi.unlinkentity; 
	   // self->nextthink = level.time + 0.1;
	//gi.unlinkentity (self);		// unlink from world
   // memset (&self, 0, sizeof(self));



	// END JOSEPH
}

/*
===============
AI_SideTrace

  Returns a 1 if a RIGHT rotation will point in a direction that is free of obstacles

  Returns -1 if LEFT, 0 otherwise
===============
*/
int AI_SideTrace( edict_t *self, float dist, float inyaw, int side )
{
	trace_t	tr;
	vec3_t	angle, vec, end, end2, mins;
	float	yaw;
	int		count=0;

	VectorCopy( self->mins, mins );
	mins[2] += 16;

	// pick a random direction
	if ((side == 1) || (!side && (random() <= 0.5)))
		yaw =  inyaw;
	else
		yaw = -inyaw;

	while (1)
	{
		VectorCopy(self->s.angles, angle);
		angle[YAW] += yaw;

		AngleVectors( angle, vec, NULL, NULL );

		// trace in this direction
		VectorMA( self->s.origin, dist, vec, end );

		tr = gi.trace( self->s.origin, mins, self->maxs, end, self, MASK_PLAYERSOLID );

		if (tr.fraction == 1)
		{

			// make sure we can see our enemy from here
			if (self->enemy)
			{
				vec3_t	oldorg;
				int		result;

				VectorCopy( self->s.origin, oldorg );
				VectorCopy( tr.endpos, self->s.origin );
	
				result = AI_ClearSight(self, self->enemy, true);

				VectorCopy( oldorg, self->s.origin );

				if (!result)
				{
					goto blocked;
				}

			}

			// check this is on ground
			VectorCopy( end, end2 );
			end2[2] -= 64;

			tr = gi.trace( end, mins, self->maxs, end2, self, MASK_PLAYERSOLID );

			if (tr.fraction < 1)
			{
				// go ahead!

				if (yaw < 0)
					return -1;	// left side
				else
					return  1;	// right side
			}

		}

blocked:

		if (count++ || !inyaw || side)
			break;

		yaw *= -1;
	}

	return 0;
}

void AI_CheckStillClimbingLadder( edict_t *self )
{
	if (self->groundentity)
	{	// we're on ground, so resume moving
		self->cast_info.currentmove = self->cast_info.move_stand;
	}
}

/*
===============
AI_EndRun

  checks to see if we should walk or run
===============
*/
void AI_EndRun( edict_t *self )
{
	if (!self->last_goal)
		return;

	if (!self->cast_info.move_run)
		return;

	if (	(self->enemy != self->last_goal)
		&&	(VectorDistance( self->s.origin, self->last_goal->s.origin ) < AI_GUARDING_DIST * 2 )
		&&	(VectorLength( self->last_goal->velocity) < 200))
	{	// close enough to walk
		self->cast_info.currentmove = self->cast_info.move_runwalk;
	}
	else if ((self->last_goal != self->goal_ent) && (!self->last_goal || self->last_goal != self->guard_ent))
	{
		self->cast_info.currentmove = self->cast_info.move_run;
	}
}

/*
===============
AI_YawTrace

  Returns a 1 if a YAW rotation from self's angles will point in a direction that is free of obstacles

  Returns 0 otherwise
===============
*/
int AI_YawTrace( edict_t *self, float dist, float inyaw )
{
	trace_t	tr;
	vec3_t	angle, vec, end, end2, mins;
	int		count=0;

	VectorCopy( self->mins, mins );
	mins[2] += 16;

	VectorCopy(self->s.angles, angle);
	angle[YAW] = anglemod(angle[YAW] + inyaw);

	AngleVectors( angle, vec, NULL, NULL );

	// trace in this direction
	VectorMA( self->s.origin, dist, vec, end );

	tr = gi.trace( self->s.origin, mins, self->maxs, end, self, MASK_PLAYERSOLID );

	if (tr.fraction == 1)
	{
		// check this is on ground
		VectorCopy( end, end2 );
		end2[2] -= 64;

		tr = gi.trace( end, mins, self->maxs, end2, self, MASK_PLAYERSOLID );

		if (tr.fraction < 1)
		{
			// go ahead!
			return  true;
		}

	}

	return false;
}

/*
=============
AI_StartRun

  Start us moving
=============
*/
void AI_StartRun( edict_t *self )
{
	// start running to them
	if ( self->maxs[2] > DUCKING_MAX_Z )
		self->cast_info.currentmove = self->cast_info.move_run;
	else
		self->cast_info.currentmove = self->cast_info.move_crwalk;
}


void AI_FreeAndClearGoalEnt( edict_t *self )
{
	if (self->owner->goal_ent == self)
		self->owner->goal_ent = NULL;

	G_FreeEdict( self );
}

/*
=============
ai_stand

Used for standing around and looking for TARGETS
Distance is for slight position adjustments needed by the animations
==============
*/
void ai_stand (edict_t *self, float dist)
{

	if (dist)
		M_walkmove (self, self->s.angles[YAW], dist);

	if (self->leader && !self->leader->active_node_data)	// we're loading up a new map
	{
		return;
	}

	if (self->cast_info.aiflags & AI_DUCKATTACK)
	{	// return to standing

		self->cast_info.aiflags &= ~AI_DUCKATTACK;

		if (self->maxs[2] < self->cast_info.standing_max_z)
		{
			self->maxs[2] = self->cast_info.standing_max_z;

			self->cast_info.currentmove = self->cast_info.move_stand_up;
			self->s.frame = self->cast_info.currentmove->firstframe;
			return;
		}
	}

	// Ridah, Check DUCKING/STANDING status
	if (self->maxs[2] < self->cast_info.standing_max_z)
	{
		if (self->cast_info.move_crstand && self->cast_info.currentmove != self->cast_info.move_crstand)
		{	// we should be ducking
			self->cast_info.currentmove = self->cast_info.move_crstand;
			self->s.frame = self->cast_info.currentmove->firstframe;
		}
		else if (self->leader && !(self->cast_info.aiflags & AI_TAKE_COVER) && (self->leader->maxs[2] > DUCKING_MAX_Z))	// can we stand now?
		{
			trace_t tr;

			self->maxs[2] = self->cast_info.standing_max_z;
			tr = gi.trace(self->s.origin, self->mins, self->maxs, self->s.origin, self, MASK_PLAYERSOLID);
			if (tr.startsolid)	// can't safely stand
			{
				self->maxs[2] = DUCKING_MAX_Z;
			}
			else
			{
				self->cast_info.currentmove = self->cast_info.move_stand;
				self->s.frame = self->cast_info.currentmove->firstframe;
			}
		}
	}
	else	// not physically ducking, so make sure our animation frame reflects this
	{
		if (	(self->cast_info.currentmove == self->cast_info.move_crstand)
			&&	(self->cast_info.move_crstand != self->cast_info.move_stand))
		{	// we should be standing
			self->cast_info.currentmove = self->cast_info.move_stand;
			self->s.frame = self->cast_info.currentmove->firstframe;

		}
	}
	// Ridah, done.

	// Ridah, Do pausetime stuff, making sure we stay paused if we have to, or resume moving otherwise
	if (!self->groundentity)
	{
		if (self->cast_info.move_jump && self->cast_info.currentmove != self->cast_info.move_jump)
			self->cast_info.currentmove = self->cast_info.move_jump;

		return;
	}
	else if (self->groundentity->use && !VectorCompare(self->groundentity->velocity, vec3_origin))
	{
		self->cast_info.pausetime = level.time + 1;
	}

	// make sure we don't abort our goal waypoint next time we try to move
	self->moveinfo.wait = level.time + 3;
	self->wait = level.time + 3;

	if (self->cast_info.pausetime > level.time)
	{	
		// see if we should wait for a lift or door
		if (self->target_ent)
		{
			if (VectorCompare(self->target_ent->velocity, vec3_origin) && VectorCompare(self->target_ent->avelocity, vec3_origin))
			{	// it's not moving, are we at our destination?

				if	(self->target_ent->use != Use_Plat)
				{
					if (self->groundentity != self->target_ent)
					{	// stopped, and we're not standing on it, so proceed
						self->target_ent = NULL;
						self->cast_info.pausetime = 0;

						return;
					}
					else if (self->target_ent->nextthink > (level.time + 0.1))
					{
						if (!self->nav_data.goal_index || (VectorDistance(level.node_data->nodes[self->nav_data.goal_index-1]->origin, self->s.origin) < 128))
						{
							self->target_ent = NULL;
							self->cast_info.pausetime = 0;

							return;
						}
					}
					else	// it's stopped, and not about to go anywhere
					{
						self->target_ent = NULL;
						self->cast_info.pausetime = 0;

						return;
					}
				}
				else if	(self->groundentity == self->target_ent)
				{
					if (self->target_ent->moveinfo.state == STATE_TOP)
					{
						self->target_ent = NULL;
						self->cast_info.pausetime = 0;

						return;
					}
				}
				else if (self->target_ent->moveinfo.state == STATE_BOTTOM)
				{
					self->target_ent = NULL;
					self->cast_info.pausetime = 0;

					return;
				}
			}

			// keep waiting
			self->cast_info.pausetime = level.time + 0.2;

		}

		if (self->groundentity && self->groundentity->use)
		{	// make sure all corners are on the platform
			trace_t tr;
			vec3_t	org, orgdest;
			float	x, y;

			for (x=-16; x<24; x+=32)
			{
				for (y=-16; y<24; y+=32)
				{
					VectorSet(org, self->s.origin[0]+x, self->s.origin[1]+y, self->s.origin[2]-23);
					VectorSet(orgdest, self->s.origin[0]+x, self->s.origin[1]+y, self->s.origin[2]-30);
					tr = gi.trace(org, NULL, NULL, orgdest, self, MASK_SOLID);

					if ((tr.fraction == 1) || (tr.ent != self->groundentity))
					{
						// set a running frame
						if (self->cast_info.move_run)
							self->s.frame = self->cast_info.move_run->firstframe 
											+	(	((int)(level.time * 10))
												 %	(self->cast_info.move_run->lastframe - self->cast_info.move_run->firstframe));

						AI_MoveToPlatCenter(self, self->groundentity);
						return;
					}
				}
			}
		}
		// should we attack someonw while waiting?
		else if (self->enemy || AI_FindTarget(self))
		{
			if (AI_BeginAttack(self))
			{
				self->cast_info.attack( self );
			}
		}
		// should we get out of the way?
		else if (self->target_ent && self->target_ent->velocity[2])
		{
			// are we within the XY axis bounds?
			if (	(	(self->s.origin[0] > (self->target_ent->absmin[0] - 20))
					 && (self->s.origin[0] < (self->target_ent->absmax[0] + 20)))
				&&	(	(self->s.origin[1] > (self->target_ent->absmin[1] - 20))
					 && (self->s.origin[1] < (self->target_ent->absmax[1] + 20))))
			{
				float	*pos;
				vec3_t	mins, maxs;

				// move out of it's way

				// set a running frame
				if (self->cast_info.move_run)
					self->s.frame = self->cast_info.move_run->firstframe 
									+	(	((int)(level.time * 10))
										 %	(self->cast_info.move_run->lastframe - self->cast_info.move_run->firstframe));

				VectorSet( mins,
					self->target_ent->absmin[0] - 20,
					self->target_ent->absmin[1] - 20,
					-9999 );

				VectorSet( maxs,
					self->target_ent->absmax[0] + 20,
					self->target_ent->absmax[1] + 20,
					9999 );

				// look for a safe position
				if (pos = NAV_GetReachableNodeOutsideBounds ( self, mins, maxs ))
				{
					vec3_t vec;

					// face this direction
					VectorSubtract( pos, self->s.origin, vec );
					VectorNormalize( vec );
					self->ideal_yaw = vectoyaw( vec );

					M_ChangeYaw( self );

					// move towards it
					M_walkmove( self, self->ideal_yaw, 10 );
				}
			}
		}

		return;
	}
	// Ridah, done.

	// Ridah, check for a reason to do something (if we're too close to our leader, or there is an enemy we should seek)
	if ((!(self->cast_info.aiflags & AI_HOLD_POSITION) || self->enemy) && self->leader && !(self->cast_info.aiflags & AI_TAKE_COVER))
	{
		if (	!(self->leader->waterlevel)			// don't follow a swimming leader
			&&	AI_FollowLeader(self, self->leader))
		{
			self->last_goal = self->leader;

			AI_EndRun(self);	// does distance checking, will start us moving

			if (self->cast_info.currentmove->frame->aifunc != ai_stand && VectorDistance(self->s.origin, self->leader->s.origin) <= AI_TOO_CLOSE_DIST)
			{	// look for a node we can reach

				AI_GetAvoidDirection( self, self->leader );
				self->cast_info.currentmove = self->cast_info.move_avoid_walk;

			}

		}

	}

	if (self->cover_ent && (self->cast_info.aiflags & AI_TAKE_COVER))
	{
		float goaldist;

		if (	self->combat_goalent
			&&	(	((goaldist = VectorDistance( self->s.origin, self->combat_goalent->s.origin )) > 64)
				 ||	(	!ValidBoxAtLoc( self->combat_goalent->s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID )
					 &&	goaldist < 128 )))
		{	// we should be running to it
			self->cast_info.currentmove = self->cast_info.move_run;
			return;
		}

		// pretend we're ducking for vis test
		self->viewheight = DUCKING_MAX_Z - 4;

		// if they can see us, and we're standing around, abort taking cover
		if (AI_ClearSight( self->cover_ent, self, false))
		{
			self->cast_info.aiflags &= ~AI_TAKE_COVER;
			self->combat_goalent = NULL;
			self->cast_info.currentmove = self->cast_info.move_run;

			return;
		}

// Ridah, 5-7-99, had to modify this to prevent Betty crouching bug
		self->viewheight = self->maxs[2] - 4;
		if (self->maxs[2] > DUCKING_MAX_Z && AI_ClearSight( self->cover_ent, self, false))
		{
			self->maxs[2] = DUCKING_MAX_Z;
			if (self->cast_info.move_crouch_down)
				self->cast_info.currentmove = self->cast_info.move_crouch_down;
		}

		return;
	}

	/*
	if (	self->goal_ent 
		&&	!self->leader
		&&	!(self->cast_info.aiflags & AI_TAKE_COVER)
		&&	(	!self->enemy
			 ||	(self->cast_info.aiflags & AI_GOAL_IGNOREENEMY)))
	*/
	if (self->goal_ent && !self->leader && !(self->cast_info.aiflags & AI_TAKE_COVER))
	{
		float goaldist;

		if (	(self->cast_info.move_run)
			&&	(	(self->cast_info.goal_ent_pausetime < level.time)))
//				 ||	(self->goal_ent->solid == SOLID_TRIGGER)))
		{
			goaldist = VectorDistance(self->s.origin, self->goal_ent->s.origin);

			if (	(self->goal_ent->touch == path_corner_cast_touch)
				||	(goaldist > 256 ))
			{
				// Ridah, fixes jerky movement in SR1 intro
				if (level.cut_scene_time)
				{
					if ((self->goal_ent->touch == path_corner_cast_touch) && (self->name_index == NAME_INTROGUY1) && (goaldist < 32))
					{	// simulate touching it
						self->goal_ent->touch( self->goal_ent, self, NULL, NULL );
						return;
					}
				}

				self->nav_data.cache_node = -1;
				self->nav_data.goal_index = 0;
				self->cast_info.currentmove = self->cast_info.move_runwalk;
				return;
			}

			if (self->goal_ent->target)
			{	// face the target (eg. the Radio in Skidrow points into the room)
				edict_t	*targ;

				if (targ = G_Find( NULL, FOFS( targetname ), self->goal_ent->target ))
				{
					if (!directly_infront( self, targ ))	// straighten up
					{
						if (self->cast_info.avoid)
						{
							self->cast_info.avoid( self, targ, true );
							return;
						}
					}
				}
				else
				{
//					gi.dprintf( "ai_stand: Unable to locate 'target' to face\n" );
				}

			}
			else if (!directly_infront( self, self->goal_ent ))	// straighten up
			{
				if (self->cast_info.avoid)
				{
					self->cast_info.avoid( self, self->goal_ent, true );
					return;
				}
			}

			if (!(self->goal_ent->cast_info.aiflags & AI_GOALENT_MANUAL_CLEAR))
			{
				self->goal_ent = NULL;	// we made it there
			}
		}
	}

	// If guarding something, and we've moved out of range, abort the pursuit
	if (self->guard_ent && !(self->cast_info.aiflags & AI_TAKE_COVER))
	{
		if (	(VectorDistance( self->s.origin, self->guard_ent->s.origin ) > self->guard_ent->guard_radius)
			||	(!AI_ClearSight(self, self->guard_ent, false)))
		{
			AI_StartRun ( self );
			return;
		}
	}

	// RAFAEL	28-dec-98
	if (AI_HearPlayer (self))
	{
		return;
	}
	
	// look for things to be hostile at
	
	if (self->enemy)
	{
		AI_StartAttack( self, self->enemy );
		return;
	}
	else if (AI_FindTarget (self))
	{
		return;
	}

	if (!(self->cast_info.aiflags & AI_TAKE_COVER) && !(self->spawnflags & 1) && (self->cast_info.idle) && (level.time > self->cast_info.idle_time))
	{
		if (self->cast_info.idle_time)
		{
			self->cast_info.idle (self);
			self->cast_info.idle_time = level.time + 15 + random() * 15;
		}
		else
		{
			self->cast_info.idle_time = level.time + random() * 15;
		}
	}

	if (	!(self->cast_info.aiflags & AI_TAKE_COVER)
		&&	!(self->goal_ent)
		&&	(self->target))
// Ridah, removed this optimization, since we are always using trigger spawned characters anyway
//		&&	(	(self->spawnflags & SPAWNFLAG_IMMEDIATE_FOLLOW_PATH)
//			 ||	(	(VectorDistance(self->s.origin, g_edicts[1].s.origin) < 800)
//				 &&	(gi.inPVS(g_edicts[1].s.origin, self->s.origin)))))	// Optimization, so we only walk if the player is close
	{	// resume following our path_corner

		self->goal_ent = G_PickTarget(self->target);
		if (!self->goal_ent)
		{
			gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
			self->target = NULL;
		}

	}

	// if we're crouched, and we can stand, then go for it
	if (self->maxs[2] < self->cast_info.standing_max_z)
	{
		if (self->stand_if_idle_time < level.time)
		{
			if (self->stand_if_idle_time > (level.time - 0.5))
			{
				self->maxs[2] = self->cast_info.standing_max_z;

				if (!ValidBoxAtLoc( self->s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID ))
					self->maxs[2] = DUCKING_MAX_Z;
				else if (self->cast_info.move_stand_up)
					self->cast_info.currentmove = self->cast_info.move_stand_up;
			}
			else
			{
				self->stand_if_idle_time = level.time + 3;
			}
		}
	}

	if ((self->cast_info.aiflags & AI_HOLD_POSITION) && !self->enemy)
	{
		// Ridah, 6-jun-99, Friendly's should avoid grenades when staying put, then come back
		if (!(self->cast_info.aiflags & AI_TAKE_COVER) || !(self->cover_ent) || (self->cover_ent->svflags & SVF_MONSTER))
		{	// we aren't avoiding anything

			if (VectorDistance( self->s.origin, self->holdpos_ent->s.origin ) >= AI_GUARDING_DIST)
			{
				self->cast_info.currentmove = self->cast_info.move_run;
			}
		}
	}

}


/*
=============
ai_walk

The monster is walking it's beat
=============
*/
void ai_walk (edict_t *self, float dist)
{
	M_MoveToGoal (self, dist);

	// check for noticing an enemy
	if (AI_FindTarget (self))
		return;

	if ((self->cast_info.search) && (level.time > self->cast_info.idle_time))
	{
		if (self->cast_info.idle_time)
		{
			self->cast_info.search (self);
			self->cast_info.idle_time = level.time + 15 + random() * 15;
		}
		else
		{
			self->cast_info.idle_time = level.time + random() * 15;
		}
	}
}


/*
=============
ai_charge

Turns towards target and advances
Use this call with a distnace of 0 to replace ai_face
==============
*/
void ai_charge (edict_t *self, float dist)
{
	vec3_t	v;

	if (self->enemy)
	{
		VectorSubtract (self->enemy->s.origin, self->s.origin, v);
		self->ideal_yaw = vectoyaw(v);
		M_ChangeYaw (self);
	}

	if (dist)
		if (!M_walkmove (self, self->s.angles[YAW], dist ))
			M_walkmove (self, self->s.angles[YAW] + 60*(rand()%3 - 1), 0.5 * dist);
}


qboolean ai_checksafeground( edict_t *self, vec3_t oldpos )
{
	vec3_t	end, start;
	trace_t	tr;
	float	x, y;

	for (x=-14; x<=14; x+=28)
	{

		for (y=-14; y<=14; y+=28)
		{

			VectorCopy( self->s.origin, start );
			start[0] += x;
			start[1] += y;

			VectorCopy( start, end );
			end[2] += self->mins[2] - 16;

			tr = gi.trace( start, vec3_origin, vec3_origin, end, self, MASK_PLAYERSOLID );

			if (tr.fraction == 1)
			{
				VectorCopy( oldpos, self->s.origin );
				gi.linkentity( self );

				return false;
			}

		}

	}

	return true;
}


/*
=============
ai_turn

don't move, but turn towards ideal_yaw
Distance is for slight position adjustments needed by the animations
=============
*/
void ai_turn (edict_t *self, float dist)
{
	vec3_t	oldpos;
//	qboolean	wassafeground;

	// wassafeground = ai_checksafeground( self, oldpos );

	VectorCopy( self->s.origin, oldpos );

	self->cast_info.aiflags &= ~AI_TURN_BLOCKED;

	if (dist > 32)
		dist = 32;

	if (self->groundentity && dist
		&&	(!(self->cast_info.aiflags & AI_NOWALK_FACE)))	// Ridah 5-8-99, fixes "aiflags 1" not working
	{
		if (!M_walkmove (self, self->s.angles[YAW], dist) && !self->leader)
		{
			self->cast_info.aiflags |= AI_TURN_BLOCKED;

			if (self->cast_info.currentmove->endfunc == AI_EndAttack)
			{	// call it now, since we can't move
				AI_EndAttack( self );
			}
		}
	}

	if (self->cast_info.avoid_ent)
	{
		// if not above safe ground, go back
		// if (wassafeground && dist && !ai_checksafeground( self, oldpos ))
		if (dist && !ai_checksafeground( self, oldpos ))
		{
			mmove_t *oldmove;

			if (self->cast_info.currentmove->endfunc == AI_EndAttack)
			{	// call it now, since we can't move
				oldmove = self->cast_info.currentmove;

				AI_EndAttack( self );

				if (self->cast_info.currentmove == oldmove)
				{
					self->cast_info.currentmove = self->cast_info.move_stand;

				}

				goto done;
			}
		}

		if (self->cast_info.last_avoid > (level.time - 1.5))
		{
			vec3_t vec;

			VectorSubtract( self->cast_info.avoid_ent->s.origin, self->s.origin, vec );
			VectorNormalize( vec );
			self->ideal_yaw = vectoyaw( vec );

			if (	(self->cast_info.currentmove->endfunc == AI_EndAttack)
				&&	(directly_infront( self, self->cast_info.avoid_ent )))
			{	// call it now, since we're facing them
				AI_EndAttack( self );
			}
		}
		else if (dist)	// been too long
		{
			self->cast_info.avoid_ent = NULL;
		}
		else	// just straighten up
		{
			vec3_t	vec;

			VectorSubtract( self->cast_info.avoid_ent->s.origin, self->s.origin, vec );
			VectorNormalize( vec );
			self->ideal_yaw = vectoyaw(vec);
		}
	}
	else if (self->enemy)
	{
		vec3_t	vec;

		VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
		VectorNormalize( vec );
		self->ideal_yaw = vectoyaw(vec);
	}

done:

	M_ChangeYaw (self);
}


void ai_turn2 (edict_t *self, float dist)
{
/*	Ridah, ai_turn does this now
	// quick fix to face the player
	if (self->enemy)
	{
		vec3_t	vec;

		VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
		VectorNormalize( vec );

		self->ideal_yaw = vectoyaw( vec );

		M_ChangeYaw( self );
	}
*/
	self->cast_info.avoid_ent = self->enemy;

	ai_turn (self, dist);
}

/*
==============
ai_sidestep

  move "dist" to the right
==============
*/
void ai_sidestep( edict_t *self, float dist)
{
	vec3_t	oldpos;

	VectorCopy( self->s.origin, oldpos );

	self->cast_info.aiflags &= ~AI_TURN_BLOCKED;

	if (dist > 32)
		dist = 32;

	if (dist)
	{
		if (!M_walkmove (self, self->s.angles[YAW] + 90, dist))
		{
			self->cast_info.aiflags |= AI_TURN_BLOCKED;

			if (self->cast_info.currentmove->endfunc == AI_EndAttack)
			{	// call it now, since we can't move
				mmove_t *om;

				om = self->cast_info.currentmove;

				AI_EndAttack( self );

				if (self->cast_info.currentmove == om)
				{
					self->cast_info.currentmove = self->cast_info.move_run;
				}
			}
		}
	}

	if (self->cast_info.avoid_ent)
	{
		// if not above safe ground, go back
		if (dist && !ai_checksafeground( self, oldpos ))
		{
			if (self->cast_info.currentmove->endfunc == AI_EndAttack)
			{	// call it now, since we can't move
				AI_EndAttack( self );
			}
		}

		if (self->cast_info.last_avoid > (level.time - 1.5))
		{
			vec3_t vec;

			VectorSubtract( self->cast_info.avoid_ent->s.origin, self->s.origin, vec );
			VectorNormalize( vec );
			self->ideal_yaw = vectoyaw( vec );
		}
		else	// been too long
		{
			self->cast_info.avoid_ent = NULL;
		}
	}
	else if (self->enemy)
	{
		vec3_t	vec;

		VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
		VectorNormalize( vec );
		self->ideal_yaw = vectoyaw(vec);
	}


	M_ChangeYaw (self);

}

/*
=============
range

returns the range catagorization of an entity reletive to self
0	melee range, will become hostile even if back is turned
1	visibility and infront, or visibility and show hostile
2	infront and show hostile
3	only triggered by damage
=============
*/
int range (edict_t *self, edict_t *other)
{
	vec3_t	v;
	float	len;

	VectorSubtract (self->s.origin, other->s.origin, v);
	len = VectorLength (v);
	if (len < MELEE_DISTANCE)
		return RANGE_MELEE;
	if (len < 500)
		return RANGE_NEAR;
	if (len < 1000)
		return RANGE_MID;
	return RANGE_FAR;
}

/*
=============
visible

returns 1 if the entity is visible to self, even if not infront ()
=============
*/
qboolean visible (edict_t *self, edict_t *other)
{
	vec3_t	spot1;
	vec3_t	spot2;
	trace_t	trace;

	VectorCopy (self->s.origin, spot1);
	spot1[2] += self->viewheight;
	VectorCopy (other->s.origin, spot2);
	spot2[2] += other->viewheight;
	trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
	
	if (trace.fraction == 1.0)
		return true;
	return false;
}


/*
=============
infront

returns 1 if other is in front (in sight) of self
=============
*/
qboolean infront (edict_t *self, edict_t *other)
{
	vec3_t	vec;
	float	dot;
	vec3_t	forward;
	
	AngleVectors (self->s.angles, forward, NULL, NULL);
	VectorSubtract (other->s.origin, self->s.origin, vec);
	VectorNormalize (vec);
	dot = DotProduct (vec, forward);
	
	if (dot > 0.2)
		return true;
	return false;
}

/*
=============
directly_infront

returns 1 if other is directly in front of self (pointing at)
=============
*/
qboolean directly_infront (edict_t *self, edict_t *other)
{
	vec3_t	vec;
	float	dot;
	vec3_t	forward, ang;
	float	len;

	VectorCopy( self->s.angles, ang );
	ang[PITCH] = 0;
	
	AngleVectors (ang, forward, NULL, NULL);
	VectorSubtract (other->s.origin, self->s.origin, vec);
	vec[2] = 0;
	len = VectorNormalize (vec);
	dot = DotProduct (vec, forward);
	
	if (dot > (0.98 - (len < 1024 ? 0.1 * (1.0 - (len/1024.0)) : 0)))
		return true;
	return false;
}

/*
=============
directly_infront_angles

returns 1 if other is directly in front of self (pointing at)
=============
*/
qboolean directly_infront_angle (vec3_t ang1, edict_t *self, edict_t *other)
{
	vec3_t	vec;
	float	dot;
	vec3_t	forward;//, ang;

//	VectorCopy( self->s.angles, ang );
//	ang[PITCH] = 0;
	
	AngleVectors (ang1, forward, NULL, NULL);
	VectorSubtract (other->s.origin, self->s.origin, vec);
//	vec[2] = 0;
	VectorNormalize (vec);
	dot = DotProduct (vec, forward);
	
	if (dot > 0.98)
		return true;
	return false;
}


//=============================================================================

/*
============
FacingIdeal

============
*/
qboolean FacingIdeal(edict_t *self)
{
	float	delta;

	delta = anglemod(self->s.angles[YAW] - self->ideal_yaw);
	if (delta > 45 && delta < 315)
		return false;
	return true;
}

//=============================================================================


//=============================================================================

extern void button_use (edict_t *self, edict_t *other, edict_t *activator);
extern void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator);
extern void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject);

// Ridah, new AI system movements functions
/*
================
AI_canmove

  tries a (CPU friendly) move to the destination point, making sure we don't move inside
  another solid, into water, or fall off an edge
================
*/
qboolean AI_canmove( edict_t *self, vec3_t dest )
{	// returns true if the dest box is valid
	trace_t tr;
	vec3_t dropdest;
	float	lift = 0;
	vec3_t	dir;

	VectorSubtract( dest, self->s.origin, dir );
	VectorNormalize( dir );

	tr = gi.trace(dest, self->mins, self->maxs, dest, self, MASK_PLAYERSOLID|CONTENTS_MONSTERCLIP);

	if (tr.startsolid || tr.allsolid)
	{	// try lifting up slightly
		dest[2] += (lift = 24);
		tr = gi.trace(dest, self->mins, self->maxs, dest, self, MASK_PLAYERSOLID | CONTENTS_MONSTERCLIP);

		if (tr.startsolid || tr.allsolid)
			dest[2] -= lift;

	}

	if (tr.startsolid || tr.allsolid)
	{
		edict_t	*activator;

		if (tr.ent == world || !(tr.ent->svflags & SVF_MONSTER))
		{	// try a line trace to find a blocking character
			tr = gi.trace(self->s.origin, NULL, NULL, dest, self, MASK_PLAYERSOLID|CONTENTS_MONSTERCLIP);
		}

		// get this character to get out of our way
		if (	(tr.fraction < 1)
			&&	(tr.ent != world) /* && [tr.ent is not an enemy (neutral or friend)]*/
			&&	(tr.ent->cast_info.avoid))
		{

			if (tr.ent->cast_info.last_avoid < (level.time - 2))
			if (	(tr.ent->cast_info.currentmove->frame[0].aifunc == ai_stand)
				||	(tr.ent->enemy && (VectorDistance(tr.ent->enemy->s.origin, tr.ent->s.origin) > 64)))
			{
				// tell them to move out the way, since they're not doing much
				tr.ent->cast_info.avoid(tr.ent, self, false);
			}

			// stop going for our current node
			self->nav_data.cache_node = -1;
			self->nav_data.goal_index = 0;

			// pause for a bit?
			if (!self->enemy)
			{
				self->cast_info.currentmove = self->cast_info.move_stand;
				self->cast_info.pausetime = level.time + 1;


			}

			return false;
		}
		else if (	(tr.ent->use)
				 &&	(tr.ent->use == button_use)
				 &&	(tr.ent->targetname)
				 &&	(!self->activator)
				 &&	(activator = G_Find( NULL, FOFS(target), tr.ent->targetname )))
		{	// go for this button
			self->activator = activator;

			// stop going for our current node
			self->nav_data.cache_node = -1;
			self->nav_data.goal_index = 0;
		}
		else if ((tr.ent->die == func_explosive_explode) && (tr.ent->takedamage == DAMAGE_YES))
		{	// blow it up
			T_Damage( tr.ent, self, self, dir, tr.endpos, vec3_origin, 50, 0, 0, MOD_UNKNOWN );
		}
		else //if (tr.ent->client || tr.ent->svflags & SVF_MONSTER)
		{	// go around it
			int res;

			res = AI_SideTrace( self, 32, 90, 0 );

			if (res < 0)
				self->ideal_yaw = self->s.angles[YAW] - 90;
			else if (res > 0)
				self->ideal_yaw = self->s.angles[YAW] - 90;

			M_ChangeYaw( self );
		}

		return false;
	}

//	if (self->waterlevel)		// swimming grunt doesn't need to check for onground
//		return true;

	VectorCopy(dest, dropdest);
	dropdest[2] -= (16 + lift);

	dest[2] += 0.1;
	tr = gi.trace(dest, self->mins, self->maxs, dropdest, self, MASK_PLAYERSOLID|CONTENTS_MONSTERCLIP);
	dest[2] -= 0.1;

	if (!tr.startsolid && !tr.allsolid && (tr.fraction < 1))
	{
		dest[2] = tr.endpos[2];
		return true;
	}
	else
	{
		dest[2] -= lift;
		return false;
	}
}

/*
=================
AI_movetogoal

  More robust movement code, which tries a CPU friendly move first, and then
  several walkmove()'s allowing for obstructions
=================
*/
qboolean AI_movetogoal (edict_t *self, edict_t *goal, float dist)
{
	vec3_t		dir, oldorg, dest;
	int			yaw, rnd;
	int			aborted=false;
	float		diff, goal_dist;
	float		slide_scale = 1.0;
	qboolean	moved=false;
	qboolean changezval = false;

/*
	if (	(self->cast_info.currentmove == self->cast_info.move_run)
		&&	(self->last_goal && self->last_goal->client)
		&&	(	(VectorLength( self->last_goal->velocity ) > dist*10)
			 ||	(VectorDistance(self->s.origin, self->last_goal->s.origin) > 256)))
	{
		dist *= 2;	// keep up with the player
		if (dist > 32)
			dist = 32;	// Ridah, they can run through walls if more than this
	}
*/
	self->flags &= ~FL_FLY;

	VectorSubtract(goal->s.origin, self->s.origin, dir);
	goal_dist = VectorNormalize(dir);
	self->ideal_yaw = vectoyaw(dir);

	// if we're walking up a steep slope, make sure we don't try and move too far
	if ((fabs(dir[2]) > 0.4) && (dist*fabs(dir[2]) > 20.0))
	{
		dist = 20.0 / fabs(dir[2]);
	}

	M_ChangeYaw(self);

	yaw = self->s.angles[YAW];

	if (dist > 4 && (diff = fabs(AngleDiff(self->s.angles[YAW], self->ideal_yaw))) > 30)
	{
//		dist *= 0.5;	// turning, don't go so far

//		if (dist < 4)
//			dist = 4;

		if (diff < 90)
			dist *= (1 - 0.8*(diff / 90));
		else
			dist *= (1 - 0.8*(90 / 90));

		// walk forwards
		AngleVectors( self->s.angles, dir, NULL, NULL );
		VectorMA( self->s.origin, dist, dir, dest );
	}
	// can we simply move towards this position?
	else if ((dist > 4) || (goal_dist < 32))
	{
		VectorMA(self->s.origin, dist, dir, dest);
	}
	else	// go forward if walking
	{
		vec3_t	fwd;

		AngleVectors( self->s.angles, fwd, NULL, NULL );
		VectorMA(self->s.origin, dist, fwd, dest);
	}

	yaw = self->s.angles[YAW];

	if (self->maxs[2] == DUCKING_MAX_Z)
	{
		self->maxs[2] = 8; //4;	// Ridah, modified as per requested
		changezval = true;
	}

	moved = AI_canmove(self, dest);

	if (changezval)
	{
		self->maxs[2] = DUCKING_MAX_Z;
	}

	if (moved)
	{
		VectorCopy(dest, self->s.origin);
		gi.linkentity(self);
		G_TouchTriggers (self);
		return true;
	}

	// nope, try walkmove()

	if ((((int) level.time) % 6) < 3)
		rnd = -1;
	else
		rnd = 1;

	VectorCopy(self->s.origin, oldorg);
	self->goalentity = goal;			// used by SV_movestep() for falling from air

	while (dist > 0)
	{
		if (M_walkmove(self, yaw,			dist))
		{
			moved=true;
			break;
		}

		if (dist > 16)
		{
			dist -= 8;
			continue;
		}

		if (!M_walkmove(self, yaw+(60*rnd), dist*0.7))
		if (!M_walkmove(self, yaw+(100*rnd), dist*0.5))
		if (!M_walkmove(self, yaw+(140*rnd), dist*0.3))
/*
		if (!M_walkmove(self, yaw-(60*rnd), dist*0.7))
		if (!M_walkmove(self, yaw-(100*rnd), dist*0.5))
		if (!M_walkmove(self, yaw-(140*rnd), dist*0.3))
*/
		{
			dist -= 3;
			continue;
		}

		break;
	}
/*
	if ((dist <= 0) && (self->maxs[2] > DUCKING_MAX_Z))
	{	// try ducking
		self->maxs[2] = DUCKING_MAX_Z;
		if (!M_walkmove(self, yaw, dist*0.5))
		{
			self->maxs[2] = self->cast_info.standing_max_z;
		}
		else
		{
			if (self->cast_info.move_crouch_down)
				self->cast_info.currentmove = self->cast_info.move_crouch_down;
		}
	}
*/
	if (aborted)
	{
		self->goalentity = NULL;
		return false;
	}

	if (dist <= 0)
	{
		return false;
	}

	// always face the direction we just went
	VectorSubtract(self->s.origin, oldorg, dir);
	VectorNormalize2(dir, dir);
	self->ideal_yaw = vectoyaw(dir);

	self->yaw_speed *= 0.25;

	M_ChangeYaw(self);

	self->yaw_speed *= 4;

	return moved;
}

/*
=================
AI_jump

  Called whenever a character needs to jump somewhere
=================
*/
void AI_jump (edict_t *self, float dist)
{
	vec3_t	forward;

	if (dist < 0)
		dist = -dist;

	// VectorCopy (self->goalentity->s.angles, self->s.angles);
	AngleVectors (self->s.angles, forward, NULL, NULL);
	VectorScale (forward, dist, self->velocity);
	self->velocity[2] = dist;

	if (self->groundentity)
		self->groundentity = NULL;
}

/*
=================
AI_climb

  Called whenever a character is climbing a ladder
=================
*/
void AI_climb (edict_t *self)
{
	static edict_t *goal = NULL;
	edict_t *save;
	
	if (!goal)
		goal = G_Spawn();

	save = self->goalentity;
	self->goalentity = goal;

	VectorCopy (self->s.origin, goal->s.origin);
	VectorCopy (self->s.angles, goal->s.angles);
	goal->s.origin[2] += 20;

//	self->enemy = goal;

	self->velocity[2] = 200;
//	ai_move (self, 0);

	self->wait = level.time + 1;
	self->moveinfo.wait = level.time + 1;
	
	if (self->nav_data.goal_index && (level.node_data->nodes[self->nav_data.goal_index-1]->origin[2] > self->s.origin[2]))
	{
		self->flags |= FL_FLY;
	}
	else	// done climbing
	{
		self->flags &= ~FL_FLY;
		AI_jump (self, 150);
		self->velocity[2] += 100;
		self->groundentity = NULL;
		self->cast_info.aiflags &= ~AI_SKILL_LADDER;
		self->cast_info.currentmove = self->cast_info.move_stand;

		self->wait = level.time + 4;
		self->moveinfo.wait = level.time + 1;
		level.node_data->nodes[self->nav_data.goal_index-1]->ignore_time = level.time + 3;	// don't go back to the landing node, or we may fall down
		self->nav_data.goal_index = 0;	// re-scan for a new path when we land
		self->nav_data.cache_node = -1;
	}
	
//	self->enemy = NULL;
	self->goalentity = save;

	self->s.frame = self->cast_info.move_stand->firstframe;
}

qboolean ValidBoxAtLoc(vec3_t org, vec3_t mins, vec3_t maxs, edict_t *ignore, int mask)
{
	trace_t tr;

	tr = gi.trace(org, mins, maxs, org, ignore, mask);

	return (!tr.allsolid);
}

void AI_GetAvoidDirection( edict_t *self, edict_t *other )
{
	vec3_t dir;
/*
	if (AI_SideTrace( self, 64, 0, 1 ))
	{
		self->ideal_yaw = entyaw( other, self );
	}
	else
*/
	if (NAV_GetAvoidDirection( self, other, dir ))
	{
		self->ideal_yaw = vectoyaw( dir );
	}
	else
	{
		self->ideal_yaw = anglemod( entyaw( other, self ) + 1.0*(rand()%90 - 45) );
	}
}

// Ridah, done.

//------------------------------------------------------------------------------
// New AI system
//
//	This is used whenever the cast member is doing something that involves moving
//	- will weigh up all current objectives, and carry out the desired action

extern void path_corner_cast_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
extern void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);

void ai_runFLASHLIGHT ( edict_t *self, float dist )
{
	trace_t tr;
	vec3_t	forward;
	vec3_t	end;
	vec3_t	mins, maxs;
	edict_t	*light;
	vec3_t	angles, origin;

	ai_run (self, dist);
	
	VectorCopy (self->s.angles, angles);
	angles[0] += crandom() * 1.5; 
	angles[1] += crandom() * 1.5; 
	angles[2] += crandom() * 1.5; 
	VectorCopy (self->s.origin, origin);
	AngleVectors (angles, forward, NULL, NULL);
	VectorCopy (origin, end);
	VectorMA (end, 8194, forward, end);

	VectorSet (mins, -8, -8, -8 );
	VectorSet (maxs, 8, 8, 8);

	tr = gi.trace (self->s.origin, mins, maxs, end, self, MASK_SHOT);

	if (tr.ent->client || tr.ent->svflags & SVF_MONSTER)
		if (!(tr.ent->flags & FL_NOTARGET))
			EP_EventSpeech (self, tr.ent, say_flashlight);

	/*
	if (tr.ent->client || tr.ent->svflags & SVF_MONSTER)
	{
		// If this is a friend, go easy on them
		if (!(tr.ent->cast_group) && (tr.ent->cast_group != self->cast_group))
			// they aren't on our side, are they a friend?
			if (!(cast_memory = level.global_cast_memory[self->character_index][tr.ent->character_index]))
			{	// record this sighting, so we can start to attack them

				AI_RecordSighting(self, tr.ent, VectorDistance(self->s.origin, tr.ent->s.origin) );
				cast_memory = level.global_cast_memory[self->character_index][tr.ent->character_index];

			}
	}
	*/

	light = G_Spawn();
	VectorCopy (tr.endpos, light->s.origin);
	light->s.renderfx |= RF_BEAM;
	light->s.effects |= EF_FLASHLIGHT;
	light->nextthink = level.time + 0.1;
	light->think = G_FreeEdict;
	gi.linkentity (light);
	
}


void ai_run ( edict_t *self, float dist )
{
	static edict_t tempgoal;
	edict_t		**goal=NULL;
	int			len;
	edict_t		tempent, *ptempent;

	route_t		route;
	node_t		*goal_node;
	vec3_t		vec, oldorg;
	int			rval, reached_goal=false;
	float		goaldist, ideal_dist;
	qboolean	moved, avoiding=false;

	if (self->cast_info.aiflags & AI_RUN_LIKE_HELL)
	{
		dist *= 1.5;
		if (dist > 32)
			dist = 32;
	}

	if (self->cast_info.move_crwalk && (self->maxs[2] < self->cast_info.standing_max_z) && (self->cast_info.currentmove != self->cast_info.move_crwalk))
	{	// we should be ducking
		self->cast_info.currentmove = self->cast_info.move_crwalk;
		self->s.frame = self->cast_info.currentmove->firstframe;
	}

	// if ducking, don't stand for at least 5 seconds after we stop
	if (self->maxs[2] < self->cast_info.standing_max_z)
	{
		self->stand_if_idle_time = level.time + 5;
	}

	tempgoal.active_node_data = level.node_data;

	if (fabs(self->s.angles[2]) > 1)
		self->s.angles[2] *= 0.8;
	else
		self->s.angles[2] = 0;

	if (self->enemy && (self->enemy->health <= 0))
	{
		self->enemy = NULL;

		if (	!(self->cast_info.aiflags & AI_TAKE_COVER)
			&&	!(self->goal_ent)
			&&	(self->cast_info.currentmove->frame->aifunc == ai_stand)
			&&	(self->start_ent)
			&&	(VectorDistance( self->s.origin, self->start_ent->s.origin) > 256))
		{	// go back to the start pos if we're not doing anything
			self->goal_ent = self->start_ent;
		}
	}

	if (self->leader && (self->leader->health <= 0))
		self->leader = NULL;

	if (!self->groundentity)
	{
		if (self->cast_info.move_jump && self->cast_info.currentmove != self->cast_info.move_jump)
			self->cast_info.currentmove = self->cast_info.move_jump;

		return;
	}

	if (self->leader && (!self->leader->active_node_data || (self->leader->waterlevel > 1)))
	{	// don't move
		self->cast_info.currentmove = self->cast_info.move_stand;

		return;
	}

	// if we've been told to stand put, only attack under special conditions
	if (	(self->leader)
		&&	(self->cast_info.aiflags & AI_HOLD_POSITION)
		&&	(!(self->cast_info.aiflags & AI_MELEE) || !self->enemy || (VectorDistance(self->s.origin, self->enemy->s.origin) > 96)))
	{
		// if running away, let us go
		if (	!(self->cast_info.aiflags & AI_TAKE_COVER)
			&&	(VectorDistance( self->s.origin, self->holdpos_ent->s.origin ) < AI_GUARDING_DIST)
			&&	(self->pain_debounce_time < (level.time - 2)))	// Ridah, 20-may-99, added this so hired guy's don't stand around while being shot at
		{
			self->cast_info.currentmove = self->cast_info.move_stand;
			self->s.frame = self->cast_info.currentmove->firstframe;
			self->cast_info.pausetime = level.time + 1.0;

			return;
		}
	}

	if (self->cast_info.aiflags & AI_SKILL_LADDER)
	{
	
		if (self->groundentity)
			self->cast_info.aiflags &= ~AI_SKILL_LADDER;
		else
			return;
	}
	else
	{
		self->flags &= ~FL_FLY;
	}

	if ((self->cast_info.aiflags & AI_HOLD_POSITION) && !self->enemy)
	{
		// Ridah, 6-jun-99, Friendly's should avoid grenades when staying put, then come back
		if (!(self->cast_info.aiflags & AI_TAKE_COVER) || !(self->cover_ent) || (self->cover_ent->svflags & SVF_MONSTER))
		{	// we aren't avoiding anything

			if (VectorDistance( self->s.origin, self->holdpos_ent->s.origin ) < AI_GUARDING_DIST)
			{
				// make sure they stop taking cover
				self->cast_info.aiflags &= ~AI_TAKE_COVER;
				self->cover_ent = NULL;

				self->cast_info.currentmove = self->cast_info.move_stand;
				self->s.frame = self->cast_info.currentmove->firstframe;

				return;
			}
			else	// run to our hold position
			{
				goal = &self->holdpos_ent;
				ideal_dist = AI_GUARDING_DIST;
				goto got_goal;
			}
		}
		else	// we are running away from something dangerous, we should return afterwards
		{
		}
	}

	if (self->cast_info.pausetime > level.time)
	{
		
		self->cast_info.currentmove = self->cast_info.move_stand;
		return;
	}
	
	if (self->groundentity && self->groundentity->use && !VectorCompare(self->groundentity->velocity, vec3_origin))
	{	// standing on a plat that's moving
		self->cast_info.pausetime = level.time + 0.5;
		self->cast_info.currentmove = self->cast_info.move_stand;

	}

	// see if we should wait for a lift or door
	if (self->target_ent)
	{
		if ( !VectorCompare(self->target_ent->velocity, vec3_origin) )
		{	// it's moving, hang around
			self->cast_info.currentmove = self->cast_info.move_stand;
			self->cast_info.pausetime = level.time + 0.2;
			return;
		}
		else if (self->target_ent->nextthink > level.time + 0.1)
		{	// it's not moving, and isn't about to start moving, so stop checking it
			self->target_ent = NULL;
		}
	}

	//===================================================================================
	//
	// Select a "goal"
	goal = &self->leader;
	ideal_dist = AI_GUARDING_DIST;

	if (	(*goal)
		&&	(AI_TooClose(self, self->leader)))
	{	// always get out of leader's way
		avoiding = true;
		goto got_goal;
	}

	if (*goal && VectorDistance( self->s.origin, (*goal)->s.origin ) > 384)
	{
		self->cast_info.aiflags |= AI_RUN_LIKE_HELL;
	}

	if (*goal && (*goal)->groundentity && (*goal)->groundentity->use)
	{
		if (!(*goal)->velocity[0] && !(*goal)->velocity[1])
		{	// standing still
			ideal_dist = AI_TOO_CLOSE_DIST;

			goto got_goal;
		}
		else	// it's moving, so we should wait
		{
			self->cast_info.pausetime = level.time + 0.2;
			self->target_ent = (*goal)->groundentity;

			self->cast_info.currentmove = self->cast_info.move_stand;


			return;
		}
	}
	// if our leader has recently issued a followme, and is far away, go to them
	else if (*goal && ((*goal)->order == ORDER_FOLLOWME) && ((*goal)->order_timestamp > (level.time - 20))
			&& (((*goal)->order_timestamp > (level.time - 5)) || (VectorDistance((*goal)->s.origin, self->s.origin) > 256)))
	{	// abort attack
		goto got_goal;
	}

enemy_again:

	if (self->enemy || (self->combat_goalent && self->cover_ent))
	{
		cast_memory_t *mem;
		edict_t	**enemy;

		// find out what we're going for
		if (self->enemy)
		{
			enemy = &self->enemy;

			if (!AI_HasLeaderButGoForEnemy(self, self->enemy))
			{
				self->enemy = NULL;
				goto enemy_again;
			}
		}
		else
		{
			enemy = &self->cover_ent;
		}

		// make sure we run
		if (self->maxs[2] == self->cast_info.standing_max_z)
			self->cast_info.currentmove = self->cast_info.move_run;

		// make sure we can see them, or have recently seen them
		if ((*enemy)->svflags & SVF_MONSTER || (*enemy)->client)
		{
			// if no memory, stop them being our enemy
			if (!(mem = level.global_cast_memory[self->character_index][(*enemy)->character_index]))
			{
				(*enemy) = NULL;
				goto enemy_again;
			}

			if (	((!self->combat_goalent) || (self->combat_goalent->think != CheckStillHiding))
				&&	((*enemy)->client && ((*enemy)->light_level <= 30))
				&&	(mem->timestamp < (level.time - 5)))
			{	// go for the last sighted position

				// DEMO HACK: send Bernie to Louie if he's alive (drastic times call for drastic measures)
				if (	(level.episode == EP_SKIDROW)
					&&	(self->name_index == NAME_ARNOLD))
				{
					edict_t	*louie;
					route_t	route;

					louie = EP_GetCharacter( NAME_LOUIE );

					// if they're near Louie, better go protect him
					if (	(louie)
						&&	(louie->health > 0)
						&&	(	!NAV_Route_EntityToEntity((*enemy), NULL, louie, VIS_PARTIAL, false, &route)
							||	(route.dist < 3000)))
					{
						self->goal_ent = louie;
						(*enemy) = NULL;
						louie->cast_info.aiflags |= AI_GOALENT_MANUAL_CLEAR;

						goto gonetolouie;
					}
				}
				// done DEMO HACK


				memset( &tempent, 0, sizeof(edict_t) );
				tempent.active_node_data = level.node_data;

				VectorCopy( mem->last_known_origin, tempent.s.origin );
				ptempent = &tempent;
				goal = &ptempent;
				VectorCopy( (*enemy)->maxs, tempent.maxs );
				VectorCopy( (*enemy)->mins, tempent.mins );

				if ((VectorDistance(self->s.origin, (*goal)->s.origin) < 128) && AI_ClearSight( self, (*goal), false ))
				{
					(*enemy) = NULL;
					goto enemy_again;
				}

gonetolouie:

				goto got_goal;
			}
		}

		goal = &(*enemy);
		ideal_dist = AI_GUARDING_DIST;

		if (	self->combat_goalent
			&&	(self->combat_goalent->inuse || (self->combat_goalent = NULL)))	// clear it if it's been freed
		{
			goal = &self->combat_goalent;

			ideal_dist = 16;	// get really close

			// if we're right infront of our enemy, abort
			if (	(self->enemy)
				&&	!(self->cast_info.aiflags & AI_GOAL_IGNOREENEMY)		// Ridah, 17-mar-99, fixes Jesus not going to threshold_target
				&&	(VectorDistance( self->s.origin, self->enemy->s.origin ) < 128)
				&&	(infront( self, self->enemy ))
				&&	(self->last_getcombatpos < (level.time - 3))
				&&	(AI_ClearSight( self->enemy, self, false )))
			{	// abort
				self->combat_goalent = NULL;
				self->cast_info.aiflags &= ~AI_TAKE_COVER;
				goto enemy_again;
			}

			if (	!(self->cast_info.aiflags & AI_MELEE)
				&&	!(self->cast_info.aiflags & AI_GOAL_IGNOREENEMY)		// Ridah, 17-mar-99, fixes Jesus not going to threshold_target
				&&	!(self->cast_info.aiflags & AI_TAKE_COVER)
				&&	((*goal)->think == G_FreeEdict)
				&&	AI_ClearSight(self, (*enemy), true))
			{
				if (self->cast_info.checkattack(self))
				{
					self->combat_goalent = NULL;
					return;		// this character has started an attack, and doesn't want to move any further
				}
			}

			if (	(self->cast_info.aiflags & AI_TAKE_COVER)
				 &&	(AI_ClearSight((*enemy), *goal, false)))
			{	// abort it
				self->cast_info.aiflags &= ~AI_TAKE_COVER;
				self->combat_goalent = NULL;
				self->dont_takecover_time = level.time + 10;
				goto enemy_again;
			}

		}
		else if (self->cast_info.checkattack)
		{	// do a character specific check for beginning an attack

			if (AI_CheckTakeCover(self))
			{
				// taking cover
//				goto (*enemy)_again;
				self->goalentity = NULL;
				self->wait = -1;
				return;
			}
			else if (AI_ClearSight(self, (*goal), true))
			{
				if (self->cast_info.checkattack(self))
				{
					self->dont_takecover_time = level.time + 5;	// attack for at least a few seconds
					return;		// this character has started an attack, and doesn't want to move any further
				}
			}
			else if (self->last_getcombatpos < (level.time - 2))
			{	// look for a good combat position
				float	*pos;

				if (pos = NAV_GetCombatPos( self, (*goal), self->cast_info.aiflags & AI_MELEE ))
				{
					edict_t *combatent;

					combatent = G_Spawn();
					VectorCopy( pos, combatent->s.origin );

					self->cast_info.aiflags &= ~AI_GOAL_IGNOREENEMY;
					self->cast_info.aiflags &= ~AI_TAKE_COVER;
					
					self->combat_goalent = combatent;
					combatent->cast_info.aiflags |= AI_GOAL_RUN;

					combatent->think = G_FreeEdict;
					combatent->nextthink = level.time + 5;	// give us some time to get there

					// go for it this frame
					goto enemy_again;
				}
			}

		}
	}
	else
	{	// look for things to be hostile at
		if ((self->s.frame == self->cast_info.currentmove->firstframe) && AI_FindTarget (self))
			goto enemy_again;
	}


	// If guarding something, and we've moved out of range, abort the pursuit
	if (self->guard_ent)
	{
		if (!(*goal))
		{	// if not doing anything else, return to the guard_ent
			if (	(VectorDistance( self->s.origin, self->guard_ent->s.origin ) > self->guard_ent->guard_radius)
				||	(!AI_ClearSight(self, self->guard_ent, false)))
//			if (VectorDistance( self->s.origin, self->guard_ent->s.origin ) > AI_GUARDING_DIST)
			{
				goal = &self->guard_ent;
				ideal_dist = AI_GUARDING_DIST;
			}
		}
		else if (	(VectorDistance( self->s.origin, self->guard_ent->s.origin ) > self->guard_ent->guard_radius)
				 &&	(!gi.inPVS(self->s.origin, self->guard_ent->s.origin))
				 &&	(!self->enemy || !AI_ClearSight(self, self->enemy, false)))		// don't go back if we can see our enemy
		{
			cast_memory_t	*mem;

			// always abort an enemy if it exists
			if (self->enemy)
			{
				mem = level.global_cast_memory[self->character_index][self->enemy->character_index];

				if (mem)
				{
					mem->ignore_time = level.time + ENEMY_SIGHT_DURATION + 0.2;	// ignore them for a bit
				}

				self->enemy = NULL;
			}

			goal = &self->guard_ent;
			ideal_dist = AI_GUARDING_DIST;
		}
	}

got_goal:

	// Ridah, 17-may-99, fixes Jesus not going to threshold_target
	if ((!(*goal) || (self->cast_info.aiflags & AI_GOAL_IGNOREENEMY)) && self->goal_ent)
	{
		goal = &self->goal_ent;

		if ((*goal)->solid == SOLID_TRIGGER)
			ideal_dist = 0;
		else if ((*goal)->dmg_radius)
			ideal_dist = (*goal)->dmg_radius;
		else
			ideal_dist = 128;
	}

	// should we press a button?
	if (self->activator)
	{
		if (self->activator->moveinfo.state != STATE_BOTTOM)
		{	// button has been pressed (probably by someone else)
			self->activator = NULL;
		}
		else	// go for the button
		{
			goal = &self->activator;
			ideal_dist = 0;
		}
	}


	//===================================================================================
	//
	// move towards this "goal"

	// always run to our attacker if on fire
	if ((self->onfiretime > 0) && (self->onfireent))
	{
		goal = &self->onfireent;
		ideal_dist = 64;
	}

	self->last_goal = *goal;

	if (!(*goal))
	{	// we don't have a goal, time for a beer

		self->cast_info.currentmove = self->cast_info.move_stand;

		return;

	}
	else	// we have a "goal"
	{

		// if it's our leader, make sure they are still reachable (not on a platform)

		if (	((*goal) == self->leader)
			&&	((*goal)->groundentity)
			&&	((*goal)->groundentity->use)
			&&	(!VectorCompare((*goal)->groundentity->velocity, vec3_origin)))
		{
			// They're on a lift, so hang around for a bit
			self->cast_info.pausetime = level.time + 2;
			self->cast_info.currentmove = self->cast_info.move_stand;
			self->s.frame = self->cast_info.currentmove->firstframe;

			
			return;
		}

		// handle ->goal_ent specially
		else if ((goal == &self->goal_ent) || (goal == &self->combat_goalent) || ((goal == &self->leader) && !avoiding))
		{
			float	goal_dist;

			goal_dist = VectorDistance( self->s.origin, (*goal)->s.origin );

			// Make sure we are running if we should be
			if (self->maxs[2] == self->cast_info.standing_max_z)
			{
				if (self->cast_info.move_runwalk && (goal == &self->leader) && (goal_dist < 256))
				{
					if (self->cast_info.currentmove != self->cast_info.move_runwalk)
					{
						self->cast_info.currentmove = self->cast_info.move_runwalk;
						self->s.frame = self->cast_info.currentmove->firstframe;
						self->cast_info.aiflags &= ~AI_RUN_LIKE_HELL;
					}
				}
				else if (((*goal) == self->enemy) || ((*goal)->cast_info.aiflags & AI_GOAL_RUN) /*&& (goal_dist > ideal_dist*2)*/)
				{
					self->cast_info.currentmove = self->cast_info.move_run;
				}
				else if (self->cast_info.move_runwalk)
				{
					self->cast_info.currentmove = self->cast_info.move_runwalk;
					self->cast_info.aiflags &= ~AI_RUN_LIKE_HELL;
				}
			}

			// should we stop moving?

			if ((*goal)->solid != SOLID_TRIGGER)
			{	// we can't touch it, so just get close to it

				if (goal_dist < ideal_dist)	// FIXME: make this configurable?
				{

					if (	(goal != &self->leader)
						||	(	((*goal)->client == NULL && (*goal)->cast_info.currentmove->frame->aifunc == ai_stand)
							 ||	((*goal)->client != NULL && VectorLength((*goal)->velocity) < 50)))
					{
						self->cast_info.currentmove = self->cast_info.move_stand;


						if ((goal == &self->goal_ent) && !(self->goal_ent->cast_info.aiflags & AI_GOALENT_MANUAL_CLEAR))
						{
							self->goal_ent = NULL;	// we made it there
						}

						if (goal == &self->combat_goalent)	// we've reached it, so clear it out
						{

							if (!(self->cast_info.aiflags & AI_TAKE_COVER))
							{
								self->combat_goalent = NULL;
							}

						}
						else if ((*goal) == self->start_ent && (*goal) == self->goal_ent)
						{
							self->goal_ent->cast_info.aiflags &= ~AI_GOAL_RUN;
							self->goal_ent = NULL;
						}

						return;
					}

				}
				else
				{
					if (	((*goal)->solid != SOLID_BBOX)
						&&	(VectorDistance( self->s.origin, (*goal)->s.origin ) < 64)
						&&	(!ValidBoxAtLoc( (*goal)->s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID )))
					{	// we can't get there
						self->cast_info.aiflags &= ~AI_TAKE_COVER;
						*goal = NULL;
						return;
					}
				}

			}

			// is it dangerous to go for it?
			else if (	(self->enemy)
					 &&	!(self->cast_info.aiflags & AI_GOAL_IGNOREENEMY)		// Ridah, 17-mar-99, fixes Jesus not going to threshold_target
					 &&	((*goal)->touch == path_corner_cast_touch)
					 &&	(	(VectorDistance( self->enemy->s.origin, (*goal)->s.origin ) < 256)
						 ||	(	(VectorDistance( self->enemy->s.origin, self->s.origin ) < 512)
							 &&	(infront(self->enemy, self)))))
			{	// simulate touching the path_corner
				(*goal)->touch( (*goal), self, NULL, NULL );
				return;
			}

			// can we walk straight towards it?
			else if (	(goal_dist < ideal_dist*2)
					 &&	(NAV_Visible( self->s.origin, (*goal)->s.origin, VIS_PARTIAL, self->maxs[2] < self->cast_info.standing_max_z ))
					 &&	(NAV_Reachable( self->s.origin, (*goal)->s.origin, (byte)self->waterlevel, (byte)(*goal)->waterlevel, (self->maxs[2] < self->cast_info.standing_max_z), REACHABLE_POOR )))
			{
				self->nav_data.cache_node = -1;
				self->nav_data.goal_index = 0;

				if (goal_dist < ideal_dist/2)
				{
					if ((*goal)->target && ((*goal)->touch == path_corner_cast_touch))
					{	// simulate touching the path_corner
						(*goal)->touch( (*goal), self, NULL, NULL );
					}
					else
					{
						if (!strcmp( (*goal)->classname, "cast_origin" ))
						{
							goal = NULL;
						}

						self->cast_info.currentmove = self->cast_info.move_stand;

						return;
					}
				}
			}

//			NAV_DrawLine( self->s.origin, (*goal)->s.origin );
		}

		else if (goal == &self->guard_ent)
		{
			// just walk back there
			if (	(self->maxs[2] == self->cast_info.standing_max_z)
				&&	(self->cast_info.currentmove == self->cast_info.move_run))
			{
				self->cast_info.currentmove = self->cast_info.move_runwalk;
			}
		}
	}

	// are we within the ideal range of our goal?

	if (	((*goal) != self->enemy)
		&&	!((*goal)->touch)
		&&	(ideal_dist > 0.1)
		&&	((goaldist = VectorDistance (self->s.origin, (*goal)->s.origin)) < ideal_dist)
		&&	NAV_Visible( self->s.origin, (*goal)->s.origin, VIS_LINE, self->maxs[2] < self->cast_info.standing_max_z ))
//		&&	AI_ClearSight(self, (*goal)))
	{
		self->cast_info.aiflags &= ~AI_IGNORE_ENEMY;

		if (	(!self->leader || ((*goal) != self->leader->moveout_ent))

			&&	(	((*goal) != self->leader)
				 ||	(!(*goal)->groundentity)
				 ||	(!(*goal)->groundentity->use)
				 ||	(!VectorCompare((*goal)->velocity, vec3_origin)))

			&&	(	(AI_FollowLeader(self, (*goal))		// if they are on a lift, and trying to move, we should avoid them, otherwise stay real close
				 &&	(	!((*goal)->groundentity)
					 ||	!((*goal)->groundentity->use)
					 ||	!VectorCompare((*goal)->velocity, vec3_origin)))))
		{	// move back, away from goal

			VectorSubtract(self->s.origin, (*goal)->s.origin, vec);

			VectorNormalize2(vec, vec);
			VectorScale(vec, 64, vec);
			VectorAdd(self->s.origin, vec, tempgoal.s.origin);

			AI_movetogoal(self, &tempgoal, dist);

			return;
		}

		self->cast_info.currentmove = self->cast_info.move_stand;

		return;
	}


	// Check for fast running (same as player)

	// Ridah, if the guy is a friendly, and he's hurt, don't run as fast
	if (self->cast_group != 1 || self->health > self->max_health/2)
	{
		if (((*goal)->client || ((*goal)->svflags & SVF_MONSTER)) || ((*goal)->cast_info.aiflags & AI_RUN_LIKE_HELL))
		{

			if (self->cast_info.aiflags & AI_RUN_LIKE_HELL)
			{
				dist*=2.5;
				if (dist > 32)
					dist = 32;	// Ridah, they can run through walls if more than this
			}
			
			// RAFAEL 30-DEC-98
			if (self->cast_info.aiflags & AI_RUSH_THE_PLAYER)
			{
				dist*=2.5;
				if (dist > 32)
					dist = 32;
			}

		}
	}

	// need to put this here because the dist for new models are > 32
	if (dist > 32)
		dist = 32;


	//===================================================================================
	//
	// We need to move somewhere

	tempgoal.nav_data.goal_index = self->nav_data.goal_index;

	goal_node = NULL;

	// find a path to the player
	if (self->nav_data.goal_index > 0)
	{
		goal_node = level.node_data->nodes[self->nav_data.goal_index-1];
		rval = ROUTE_INDIRECT;
	}
	else
	{

		rval = NAV_Route_EntityToEntity(self, goal_node, (*goal), VIS_PARTIAL, false, &route);

		// update current node
		self->nav_data.goal_index = tempgoal.nav_data.goal_index = route.path+1;

		if (rval == ROUTE_INDIRECT)
		{
			goal_node = level.node_data->nodes[self->nav_data.goal_index-1];

			// is it a crouching node?
			if (goal_node->node_type & NODE_DUCKING)
			{
				if (self->maxs[2] > DUCKING_MAX_Z)
				{
					if (self->cast_info.move_crouch_down)
						self->cast_info.currentmove = self->cast_info.move_crouch_down;
					self->maxs[2] = DUCKING_MAX_Z;

					return;
				}
			}

			// allow 5 seconds to reach the new target
			self->wait = level.time + 5;
		}
		else if (rval == ROUTE_DIRECT)
		{
// Disabled this, if we have a direct route, then we don't need to change this
/*
			if (((*goal)->solid == SOLID_BBOX) && ((*goal)->maxs[2] < (*goal)->cast_info.standing_max_z) && (self->maxs[2] > DUCKING_MAX_Z))
			{
				if (self->cast_info.move_crouch_down)
					self->cast_info.currentmove = self->cast_info.move_crouch_down;
				self->maxs[2] = DUCKING_MAX_Z;

				return;
			}
*/
			self->wait = level.time + 3.5;
		}
	}

	
	self->last_rval = rval;


	if (!rval)
	{	// no route
		int	max_cnt=50;

		if ((goal != &self->enemy) || !(self->cast_info.aiflags & AI_MELEE) || (self->cast_info.aiflags & AI_NO_TAKE_COVER))
		{	// resort to id's movement code (YUCK!)
			self->goalentity = (*goal);
			M_MoveToGoal (self, dist);
		}
		else // hide until we can reach them again
		{
			AI_ForceTakeCover( self, self->enemy, true );
			self->cast_info.aiflags &= ~AI_TAKECOVER_IGNOREHEALTH;
		}

		if (nav_aipath->value && (*goal))
		{
			gi.dprintf( "%s", self->classname );
			if (self->name)
				gi.dprintf( " (%s)", self->name );
			gi.dprintf( " has no route to %s\n", self->classname, (*goal)->classname );
		}

		if (gi.inPVS( g_edicts[1].s.origin, self->s.origin ))
		{
			max_cnt = 5;
		}

		if (self->noroute_count++ > max_cnt)		// keep trying for some time
		{
			if (/*(*goal) ==*/ self->enemy)		// don't bother going for them if there isn't a route
			{
				self->combat_goalent = NULL;
				AI_ForceTakeCover( self, self->enemy, false );

//				if (self->cast_info.aiflags & AI_TAKE_COVER)
					return;
			}
			else if (self->combat_goalent && (*goal) == self->combat_goalent)
			{
				self->combat_goalent = NULL;
				return;
			}

			// stop going for whatever we were going for
			self->cast_info.pausetime = level.time + 2;
			self->cast_info.currentmove = self->cast_info.move_stand;
			self->s.frame = self->cast_info.currentmove->firstframe;

			return;
		}

		goto done;

	}
	else
	{
		self->noroute_count = 0;
	}

	// successfully found a route

	if (rval == ROUTE_DIRECT)
	{	// we can reach the "goal" directly
		float	goal_dist;

		goal_dist = VectorDistance( self->s.origin, (*goal)->s.origin );

		if (goal_dist < dist)
			dist = goal_dist;

		self->goalentity = (*goal);
		AI_movetogoal(self, (*goal), dist);

		if (!(*goal))	// it's been cleared somewhere
			return;

		if (nav_aipath->value)
		{
			NAV_DrawLine( self->s.origin, (*goal)->s.origin );
		}

		goto done;
	}

	// indirect route

// =============================================================================================

// FIXME: if goal is a character, and we are not within gi.PHS() or gi.PVS(), then we can't 
// follow them, since we can't see or hear them. Therefore, we should either go to their last
// sighted position, or give up on them.

// =============================================================================================

	VectorCopy(goal_node->origin, tempgoal.s.origin);
	tempgoal.s.origin[2] = self->s.origin[2];
	len = VectorDistance(self->s.origin, tempgoal.s.origin);
	tempgoal.s.origin[2] = goal_node->origin[2];

	if (len <= dist*1.2)
	{	// reached the waypoint
		reached_goal = true;
		dist = len;
	}

	if (nav_aipath->value)
	{
		NAV_DrawLine( self->s.origin, tempgoal.s.origin );
		NAV_Debug_DrawPath( &tempgoal, (*goal) );
	}

	//===================================================================================
	//
	// are we about to reach the "goal"?

	if (reached_goal)
	{

		// should we JUMP?

		if (goal_node->node_type & NODE_JUMP)
		{

			// do we really want to jump here?
			if (	(NAV_Route_NodeToEntity(goal_node, (*goal), VIS_PARTIAL, &route) == ROUTE_INDIRECT)
				&&	(route.path == goal_node->goal_index))
			{

				if ((level.node_data->nodes[goal_node->goal_index]->origin[2] - 54) > goal_node->origin[2])
				{		// must be a ladder

	
					// note to Ryan jun-07-99 
					// The dogs can go up and down ladders. Can this be stopped? 
					
					// move us to the ladder if possible, if not abort climbing
					/*
					if (ValidBoxAtLoc( tempgoal.s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID ))
					{
						VectorCopy( tempgoal.s.origin, self->s.origin );

						self->s.angles[YAW] = self->ideal_yaw = (float) goal_node->yaw;
						self->cast_info.aiflags |= AI_SKILL_LADDER;
						self->nav_data.goal_index = route.path+1;
						AI_climb (self);

						if (self->cast_info.move_start_climb)
							self->cast_info.currentmove = self->cast_info.move_start_climb;
					}
					else	// abort running for a bit
					{
						self->cast_info.currentmove = self->cast_info.move_stand;
						self->cast_info.pausetime = level.time + 1.0;
						self->nav_data.goal_index = 0;

						return;
					}
					*/
					
					if ((self->gender) && ValidBoxAtLoc( tempgoal.s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID ))
					{
						VectorCopy( tempgoal.s.origin, self->s.origin );

						self->s.angles[YAW] = self->ideal_yaw = (float) goal_node->yaw;
						self->cast_info.aiflags |= AI_SKILL_LADDER;
						self->nav_data.goal_index = route.path+1;
						AI_climb (self);

						if (self->cast_info.move_start_climb)
							self->cast_info.currentmove = self->cast_info.move_start_climb;
					}
					else	// abort running for a bit
					{
						self->cast_info.currentmove = self->cast_info.move_stand;
						self->cast_info.pausetime = level.time + 1.0;
						self->nav_data.goal_index = 0;

						return;
					}

				}
				else	// normal jump
				{
					vec3_t neworg;
					float	speed;

					self->flags &= ~FL_FLY;

					VectorSubtract(level.node_data->nodes[goal_node->goal_index]->origin, goal_node->origin, vec);
					VectorNormalize2(vec, vec);
					self->s.angles[YAW] = vectoyaw(vec);

					if ((speed = VectorLength(goal_node->jump_vel)) > 250)
					{
						vec3_t	vec;
						float	length;

						AI_jump (self, 200);

						VectorCopy(goal_node->jump_vel, vec);

						self->velocity[2] = 0;
						vec[2] = 0;

						length = VectorLength( vec );

						if (length < 20)
						{
							VectorNormalize( vec );
							VectorScale( vec, 20, vec );
							
							goal_node->jump_vel[0] = vec[0];
							goal_node->jump_vel[1] = vec[1];

							level.node_data->modified = true;
						}
						else
						{
							VectorNormalize( self->velocity );
							VectorScale( self->velocity, length, self->velocity );
						}

						self->velocity[2] = goal_node->jump_vel[2];

						if (goal_node->jump_vel[2] > 200)
							self->velocity[2] = 300;
						else if (goal_node->jump_vel[2] < 180)
							self->velocity[2] = 180;
					}
					else
					{
						AI_jump (self, 200);
						self->velocity[2] = 200;
					}

					VectorCopy(self->s.origin, neworg);
					neworg[2] += 1;
					if (ValidBoxAtLoc(neworg, self->mins, self->maxs, self, MASK_PLAYERSOLID))
						self->s.origin[2] += 1;

					self->groundentity = NULL;

					if (self->cast_info.move_jump)
						self->cast_info.currentmove = self->cast_info.move_jump;

					// we need a way of playing character specific jump sounds
					//gi.sound(self, CHAN_VOICE, gi.soundindex("*jump1.wav"), 0.8, ATTN_NORM, 0);

					gi.linkentity(self);
				}

				self->nav_data.goal_index = goal_node->goal_index + 1;
				reached_goal = false;

				goto done;
			}

		}

	}

	//===================================================================================
	//
	// Perform the move towards the goal node

	VectorCopy(self->s.origin, oldorg);
	
	// move towards the node
	moved = AI_movetogoal(self, &tempgoal, dist);

	//...................................................................................
	// Evaluate the move

	// did we get closer?
	if (moved)
	{
		self->moveinfo.wait = level.time + 0.4;
	}
	else if (	(self->moveinfo.wait < level.time)
			 ||	(self->wait < level.time)
			 ||	(!ValidBoxAtLoc( tempgoal.s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID)))
	{	// abort this node, we've been trying to get to it for long enough

		goal_node->ignore_time = level.time + 3;
		self->nav_data.cache_node = -1;
		self->moveinfo.wait = level.time + 0.7;		// give some time on the next node
		self->wait = level.time + 3;
		self->nav_data.goal_index = 0;
		reached_goal = false;

	}
	else // will a jump be useful here?
	{
		if ((self->s.origin[2] + 16 < (&tempgoal)->s.origin[2]))
		{
			vec3_t start, mins, dir, org;
			trace_t	trace;

			VectorCopy(self->s.origin, start);

			AngleVectors(self->s.angles, dir, NULL, NULL);
			VectorScale(dir, 32, dir);
			VectorAdd(start, dir, org);

			VectorCopy(self->mins, mins);
			mins[2] += 8;

			trace = gi.trace(start, mins, self->maxs, org, self, MASK_PLAYERSOLID);

			if ((trace.fraction < 1) && !(trace.ent->svflags & SVF_MONSTER) && (fabs(trace.plane.normal[2]) < 0.1))
			{
				// set ideal velocity
				dir[2] = 310;

				// see if clear at head
				start[2] += 32;
				org[2] += 32;

				trace = gi.trace(start, NULL, NULL, org, self, MASK_SOLID);

				if (trace.fraction == 1)
				{	// safe to jump
					AI_jump (self, 150);
					self->velocity[2] += 100;

					if (self->cast_info.move_jump)
						self->cast_info.currentmove = self->cast_info.move_jump;
				}
			}
		}
	}

done:

	if (	(self->maxs[2] < self->cast_info.standing_max_z)
		&&	(	(goal_node && !(goal_node->node_type & NODE_DUCKING))
			 ||	(rval == ROUTE_DIRECT)))
	{	// we should return to standing, if possible
		trace_t tr;

		self->maxs[2] = self->cast_info.standing_max_z;
		tr = gi.trace(self->s.origin, self->mins, self->maxs, self->s.origin, self, MASK_PLAYERSOLID);
		if (	tr.startsolid
			||	(goal_node && !NAV_Visible( self->s.origin, goal_node->origin, VIS_PARTIAL, false ))
			||	((rval == ROUTE_DIRECT) && (*goal) && !NAV_Visible( self->s.origin, (*goal)->s.origin, VIS_PARTIAL, false )))
		{	// can't safely stand
			self->maxs[2] = DUCKING_MAX_Z;
		}
		else if (self->cast_info.move_stand_up)	// ok to stand
		{
//gi.dprintf("2\n");
			self->cast_info.currentmove = self->cast_info.move_stand_up;
			self->s.frame = self->cast_info.currentmove->firstframe;
			return;
		}
		else
		{
			self->cast_info.currentmove = self->cast_info.move_run;
		}
	}

	if (reached_goal)
	{

		// press a button?
		if (	(goal_node->node_type & NODE_BUTTON)
			&&	(goal_node->goal_ent)
			&&	((goal_node->goal_ent->timestamp < (level.time - 4)) || (goal_node->goal_ent->activator != self))
			&&	(goal_node->goal_ent->use))
		{	// simulate touching it
			goal_node->goal_ent->use( goal_node->goal_ent, self, self );

			self->cast_info.pausetime = level.time + 2;	// wait for it to do something

			// walk backwards
			self->cast_info.currentmove = self->cast_info.move_avoid_reverse_walk;
			self->ideal_yaw = (float)goal_node->yaw;	// don't turn

			self->activator = NULL;		// stop going for a button

			return;
		}

		if (	(goal_node->node_type & NODE_PLAT)
			&&	(self->groundentity == goal_node->goal_ent)
			&&	(VectorCompare( self->groundentity->velocity, vec3_origin )))
		{
			// we're standing on it, so trigger it
			if (self->groundentity->targetname)
			{
				edict_t *btn=NULL, *btn2=NULL;
				char	*targetname;

				targetname = self->groundentity->targetname;

				while (btn = G_Find( btn, FOFS( target ), targetname ))
				{
					if (!strcmp(btn->classname, "trigger_relay") && btn->targetname)
					{	// check this target

						btn2 = NULL;
						while (btn2 = G_Find( btn2, FOFS( target ), btn->targetname ))
						{
							if (fabs( btn2->absmin[2] - self->s.origin[2] ) < 64)
							{
								btn2->use( btn2, self, self );
								break;
							}
						}
					}
					else if (btn->solid == SOLID_BSP)
					{
						if (fabs( btn->absmin[2] - self->s.origin[2] ) < 64)
						{
							btn->use( btn, self, self );
							break;
						}
					}
				}
			}
			else
			{
				self->groundentity->use( self->groundentity, self, self );
			}

			return;
		}

		if (!(*goal))
		{	// it's been cleared
			return;
		}

		// find the next waypoint
		rval = NAV_Route_EntityToEntity( self, goal_node, (*goal), VIS_PARTIAL, false, &route);

		self->nav_data.goal_index = tempgoal.nav_data.goal_index = route.path+1;

		if (rval == ROUTE_INDIRECT)
		{
			int i, temp_rval, rval2;
			node_t	*temp_node;

			// check for a plat along the route that's not ready
			temp_node	= goal_node;
			i			= 0;
			while ((temp_rval = NAV_Route_NodeToEntity( temp_node, (*goal), VIS_PARTIAL, &route)) && (i++ < 3))
			{
				if (temp_rval == ROUTE_DIRECT)
					break;

				temp_node = level.node_data->nodes[route.path];
				if (temp_node->node_type & NODE_PLAT)
				{
					if (	temp_node->goal_ent
						&&	VectorCompare(temp_node->goal_ent->velocity, vec3_origin))
					{	// platform is stationary
						trace_t tr;
						vec3_t	src, dest;

						// make sure this lift will take us to the goal
						if (	!(rval2 = NAV_Route_NodeToEntity( temp_node, (*goal), VIS_PARTIAL, &route))
							||	(rval2 == ROUTE_DIRECT)
							||	(route.path != temp_node->goal_index))
						{
							break;	// we don't want to go on the lift
						}

						// make sure it is waiting for us

						VectorCopy(temp_node->origin, dest);
						dest[2] -= 1;
						VectorCopy(temp_node->origin, src);
						src[2] += 1;

						tr = gi.trace(src, self->mins, self->maxs, dest, self, MASK_SOLID);

						if (!tr.startsolid && (tr.ent == temp_node->goal_ent) && (tr.fraction < 1))
							break;	// cool, it's there waiting for us

						// hmm, it's not waiting, should we press a button for it to come back down?
						if (temp_node->goal_ent->targetname)
						{
							edict_t *btn=NULL, *btn2=NULL;
							char	*targetname;

							targetname = temp_node->goal_ent->targetname;

							while (btn = G_Find( btn, FOFS( target ), targetname ))
							{
								if (!strcmp(btn->classname, "trigger_relay") && btn->targetname)
								{	// check this target

									btn2 = NULL;
									while (btn2 = G_Find( btn2, FOFS( target ), btn->targetname ))
									{
										if (fabs( btn2->absmin[2] - self->s.origin[2] ) < 64)
										{
											btn2->use( btn2, self, self );
											break;
										}
									}
								}
								else if (btn->solid == SOLID_BSP)
								{
									if (fabs( btn->absmin[2] - self->s.origin[2] ) < 64)
									{
										btn->use( btn, self, self );
										break;
									}
								}
							}
						}
					}

					// damn, route is broken, so hang around for a bit
					self->cast_info.pausetime = level.time + 2;

					self->target_ent = temp_node->goal_ent;
					self->cast_info.currentmove = self->cast_info.move_stand;
					self->nav_data.goal_index = tempgoal.nav_data.goal_index = goal_node->index+1;		// check again when done waiting

					return;
				}
			}

			self->wait = level.time + 3;
			goal_node = level.node_data->nodes[self->nav_data.goal_index-1];

			// is it a crouching node?
			if (goal_node->node_type & NODE_DUCKING)
			{
				if (self->maxs[2] == self->cast_info.standing_max_z)
				{
					if (self->cast_info.move_crouch_down)
						self->cast_info.currentmove = self->cast_info.move_crouch_down;
					self->maxs[2] = DUCKING_MAX_Z;
				}
			}

		}
	}
	else if (goal_node && !(goal_node->node_type & NODE_DUCKING) && (self->maxs[2] < self->cast_info.standing_max_z))
	{	// can we stand?
		if (NAV_Visible( self->s.origin, goal_node->origin, VIS_PARTIAL, false ))
		{
			self->maxs[2] = self->cast_info.standing_max_z;
			if (self->cast_info.move_stand_up)
			{
				self->cast_info.currentmove = self->cast_info.move_stand_up;
				self->s.frame = self->cast_info.currentmove->firstframe;
				return;
			}
		}
	}

	self->goalentity = (*goal);
}

void ai_runDOKEY (edict_t *self, float dist)
{
	AI_movetogoal(self, self->goal_ent, dist);
}
