("sprites/s_smoke4.sp2");
("sprites/s_smoke4.sp2");
("sprites/s_bfg3.sp2");
("sprites/s_bfg1.sp2");
("sprites/s_photon.sp2");
("sprites/plasma.sp2");
("sprites/plasma.sp2");
("sprites/s_bfg1.sp2");
("sprites/blood/blood1.sp2");
("sprites/s_light.sp2");
("sprites/s_explod.sp2"); 
("sprites/Drexp.sp2"); 


AI_RecordSighting( self, ent, 64 );
level.global_cast_memory[newent->character_index][ent->character_index]->flags |= (MEMORY_HIRED | MEMORY_HIRE_FIRST_TIME | MEMORY_HIRE_ASK);



MEMORY_PERSONAL_OPINION // set this if we have a personal vendetta on the character, which is not necessarily that of the group
MEMORY_CONFIDENTIAL // only share this information to a fellow group member, or under extreme conditions (like we are neutral and are bribed, or we are tortured)
MEMORY_HOSTILE_ENEMY// attack upon sighting this character
MEMORY_ASSHOLE // we don't like this person, but they're not an enemy (just effects speech)
MEMORY_LASTWARNING // we want to give a final warning to the player before we become hostile
MEMORY_UPSET// will use specific upset speech

MEMORY_AFRAID
MEMORY_TAUNT
MEMORY_WARNED_BACKOFF // we've warned this person to backoff already

MEMORY_HIRE_FIRST_TIME
MEMORY_HIRE_ASK
MEMORY_HIRED
MEMORY_NO_TALK // don't talk to this person
MEMORY_STARTED_ATTACK	// so AI friends only attack enemies that are attacking (and not evading)


void AI_RecordSighting(edict_t *src, edict_t *dest, float dist)
{
	cast_memory_t *cast_memory, *dest_enemy_memory, *dest_to_dest_enemy_memory;
	qboolean	processed=false;


	if (ai_debug_memory->value)
	{
		// draw a line to show that we can see them
		NAV_DrawLine( src->s.origin, dest->s.origin );
	}

	cast_memory = level.global_cast_memory[src->character_index][dest->character_index];

	if (!cast_memory)
	{	// we need to create the memory of this character
		AI_CreateCharacterMemory(src, dest);
		cast_memory = level.global_cast_memory[src->character_index][dest->character_index];
	}

	// do we have a sight target?
	if (	(cast_memory->memory_type == MEMORY_TYPE_ENEMY)
		&&	(src->sight_target)
		&&	(!src->goal_ent)
		&&	(cast_memory->timestamp < (level.time - 10)))	// we haven't seen them in a while
	{
		edict_t	*targ;

		targ = NULL;
		if (targ = G_Find (NULL, FOFS(targetname), src->sight_target))
		{
			src->goal_ent = targ;
			src->cast_info.aiflags |= AI_GOAL_IGNOREENEMY;
			gi.dprintf( "AI: Going for sight_target\n");
		}
	}


	// Check for special events
	processed = EP_CastSight ( src, dest, cast_memory );


	// update the memory of this character

	VectorCopy(dest->s.origin, cast_memory->last_known_origin);
	cast_memory->timestamp = level.time;
	cast_memory->timestamp_dist = dist;

	cast_memory->last_known_closest_node = dest->nav_data.cache_node;


	// if they're a friend, share enemy information
	if (	(cast_memory->memory_type == MEMORY_TYPE_FRIEND)
		&&	(	(src->cast_group != 1)
			 ||	(!dest->client && dest->leader)	// if not a client, only help if they've been hired
			 ||	(cast_memory->flags & MEMORY_HIRED)))
	{

		AI_ShareEnemies( src, dest );

	}

	else if (processed)		// EP_CastSight() has processed the reactions of this AI character
	{

		return;

	}

	/*
	else if (cast_memory->flags & MEMORY_TAUNT)
	{
		src->cast_info.currentmove = src->cast_info.move_stand;
	}
	*/
	
	// Below checks for making them HOSTILE
	
	else if (cast_memory->flags & MEMORY_HOSTILE_ENEMY)
	{

		// already hostile

		// if currently hiding from someone else, then abort hiding
		if (	(src->cast_info.aiflags & AI_TAKE_COVER)
			&&	(dest != src->cover_ent))
		{
			src->cast_info.aiflags &= ~AI_TAKE_COVER;
			src->cast_info.aiflags &= ~AI_TAKECOVER_IGNOREHEALTH;
		}

		// if we've been told to hold position, and we're melee, then hide
		else if (	!(src->cast_info.aiflags & AI_TAKE_COVER)
				&&	(src->cast_info.aiflags & AI_HOLD_POSITION)
				&&	(src->cast_info.aiflags & AI_MELEE))
		{
			AI_ForceTakeCover( src, dest, true );
		}


	}

	// If our leader has just issued a Moveout order, then we should attack it
	else if (	(src->leader)
			&&	(src->cast_info.aiflags & AI_MOVEOUT)
			&&	(src->leader->order == ORDER_MOVE)
			&&	(src->leader->moveout_ent == dest)
			&&	(dest->health > 0))
	{

		AI_MakeEnemy( src, dest, 0 );
		src->enemy = dest;

// Ridah, changed this, they should keep taking moveout orders until we tell them otherwise
//		src->cast_info.aiflags &= ~AI_MOVEOUT;

	}

	// Are they in our territory?
	else if (	(dest->last_territory_touched)
			 &&	(dest->last_territory_touched->cast_group == src->cast_group)
			 &&	(dest->last_territory_touched->moral > MORAL_HAPPY))
	{

		//  the player can have some time to get out of the
		//  territory if his weapon is holstered		

		if (	(	(dest->noise_time > (level.time - 1))
				 &&	(dest->noise_type == PNOISE_WEAPON))

			||	( VectorDistance(dest->s.origin, dest->last_territory_touched->pos1) > dest->last_territory_touched->dmg_radius )

			// problem with the ai_territory
			// ||	(dest->client && !(dest->client->pers.holsteredweapon) && dest->client->pers.weapon)
			// && (dist < AI_NOT_HOLSTERED_RANGE_2)

			||	(	(dist < AI_NOT_HOLSTERED_RANGE_1)
				 &&	(!(cast_memory->flags & MEMORY_HOSTILE_ENEMY))
				 &&	(	(!dest->client)
					 ||	(	(!dest->client->pers.holsteredweapon)
						 &&	(dest->client->pers.weapon)))))
		{	// always attack
			AI_MakeEnemy( src, dest, 0 );

			if	(dest->client && !(dest->client->pers.holsteredweapon) && dest->client->pers.weapon)
			{
				if (src->gender == GENDER_FEMALE)
					Voice_Random( src, dest, f_profanity_level3, F_NUM_PROFANITY_LEVEL3 );
				else
					Voice_Random( src, dest, fightsounds, 10 );
			}
			else
			{
				if (src->gender == GENDER_FEMALE)
					Voice_Specific( src, dest, f_fightsounds, 1 );	// FUCKER!!
				else
					Voice_Specific( src, dest, fightsounds, 9 );	// FUCKER!!
			}

			if (ai_debug_memory->value)
			{
				gi.dprintf("AI_RecordSighting: Weapon not holstered, attacking!\n");
			}
		}
		else if (dist < AI_NOT_HOLSTERED_RANGE_3)
		{	// let them know we're aware of them

			#define	TIME_TO_COMPLY	4.0

			if (!(cast_memory->flags & MEMORY_HOSTILE_ENEMY))
			{
				if (	(level.time > cast_memory->not_holstered_attack_time)
					&&	(level.time < (cast_memory->not_holstered_attack_time+5)))
				{	// attack, we've already warned them
					AI_MakeEnemy( src, dest, 0 );

					if (ai_debug_memory->value)
					{
						gi.dprintf("AI_RecordSighting: Weapon not holstered, attacking!\n");
					}
				}
				else if (level.time > cast_memory->not_holstered_attack_time)
				{

					//gi.dprintf( "SOUND TODO: GET OUT OF OUR TERRITORY PUNK!\n" );

					// FIXME: we need a list of voices to choose from here

					// there sounds should only play is dest can see src

					if (visible (src, dest) && infront (src, dest))
					{
					if (src->gender == GENDER_FEMALE)
					{
						//extern voice_table_t f_backoff[];
						Voice_Random(src, dest, f_backoff, 3);
					}
					else
					{
						//extern voice_table_t m_backoff[];
						
						// JOSEPH 26-MAY-99
						if (src->name_index == NAME_NICKIBLANCO)
						{
							Voice_Specific(src, src->cast_info.talk_ent, nickiblanco, 10);							
						}						
						else if (src->name_index == NAME_TYRONE)
						{
							Voice_Specific(src, src->cast_info.talk_ent, ty_tyrone, 10);							
						}	
						else if (src->name_index == NAME_MOKER)
						{
							Voice_Specific(src, src->cast_info.talk_ent, steeltown_moker, 10);							
						}							
						else if (src->name_index == NAME_JESUS)
						{
							Voice_Specific(src, src->cast_info.talk_ent, sr_jesus, 10);							
						}	
						else if (src->name_index == NAME_HEILMAN)
						{
							Voice_Specific(src, src->cast_info.talk_ent, heilman, 10);							
						}	
						else if (src->name_index == NAME_BLUNT)
						{
							Voice_Specific(src, src->cast_info.talk_ent, blunt, 10);							
						}	
						else if (src->name_index == NAME_KINGPIN)
						{
							Voice_Specific(src, src->cast_info.talk_ent, kingpin, 10);							
						}	
						else
							Voice_Random(src, dest, specific, 2);
						// END JOSEPH
					}
					}

					// give them some "time to comply"
					cast_memory->not_holstered_attack_time = level.time + TIME_TO_COMPLY;

					if (ai_debug_memory->value)
					{
						gi.dprintf("AI_RecordSighting: Weapon not holstered, you have %i seconds to comply\n", (int) TIME_TO_COMPLY);
					}

					// if we're standing around, turn to face them
					if (	(src->cast_info.currentmove->frame->aifunc == ai_stand)
						&&	(src->cast_info.move_avoid_walk && src->cast_info.move_avoid_crwalk))
					{
						vec3_t vec;

						VectorSubtract( dest->s.origin, src->s.origin, vec );
						VectorNormalize( vec );
						src->ideal_yaw = vectoyaw( vec );

						if (src->maxs[2] < src->cast_info.standing_max_z)
							src->cast_info.currentmove = src->cast_info.move_avoid_crwalk;
						else
							src->cast_info.currentmove = src->cast_info.move_avoid_walk;
					}

				}
			}

			// if we're standing around, turn to face them
			if (src->cast_info.currentmove->frame->aifunc == ai_stand)
			{
				vec3_t vec;

				VectorSubtract( dest->s.origin, src->s.origin, vec );
				VectorNormalize( vec );
				src->ideal_yaw = vectoyaw( vec );

				M_ChangeYaw( src );
			}
		}
/*
		else if (dist < AI_NOT_HOLSTERED_RANGE_3)
		{	// if walking, ignore, if running, turn to face them, ready for attack if they get within range

			if (VectorLength( dest->velocity ) > 210)
			{	// running
				
				// if we're standing around, turn to face them
				if (src->cast_info.currentmove->frame->aifunc == ai_stand)
				{
					vec3_t vec;

					VectorSubtract( dest->s.origin, src->s.origin, vec );
					VectorNormalize( vec );
					src->ideal_yaw = vectoyaw( vec );

					M_ChangeYaw( src );
				}

			}

		}
*/
		if (ai_debug_memory->value)
		{
			gi.dprintf("AI_RecordSighting: Get the FUCK off my street!\n");
		}

	}

	// are they attacking one of our friends (client only)?
	else if (	(dest->enemy)
			 &&	(dest->enemy->client)
			 &&	(dest->enemy->health > 0)
			 &&	(dest_to_dest_enemy_memory = level.global_cast_memory[dest->character_index][dest->enemy->character_index])
			 &&	(dest_to_dest_enemy_memory->flags & MEMORY_STARTED_ATTACK)
			 &&	(dest_enemy_memory = level.global_cast_memory[src->character_index][dest->enemy->character_index])
			 &&	(	(src->cast_group != 1)
				 ||	(!dest->enemy->client && dest->enemy->leader)	// if not a client, only help if they've been hired
				 ||	(dest_enemy_memory->flags & MEMORY_HIRED))
			 &&	(dest_enemy_memory->memory_type == MEMORY_TYPE_FRIEND))
	{	// hey, leave our homie alone!

		AI_MakeEnemy( src, dest, 0 );

		if (ai_debug_memory->value)
		{
			gi.dprintf("AI_RecordSighting: Hey, leave our homie alone!\n");
		}

	}

	// if they have recently fired a weapon, we should get outta here (since we don't want to get involved)
	else if (	!src->enemy
			&&	(!src->cast_group || (dest->current_territory != src->cast_group))
			&&	(src->moral < MORAL_PSYCOTIC)
			&&	(dest->noise_time > (level.time - 3) && dest->noise_type == PNOISE_WEAPON)
			&&	(!src->leader || (src->cast_info.aiflags & AI_HOLD_POSITION))
			&&	!src->goal_ent
			&&	(!(src->cast_info.aiflags & AI_TAKE_COVER) || (VectorDistance(src->s.origin, src->combat_goalent->s.origin) < 256))
			&&	(!directly_infront_angle(dest->noise_angles, dest, src)))
//			&&	(AI_ClearSight(src, dest, false)))
	{

		if (AI_ForceTakeCover( src, dest, true ))
		{
		}
		else if (src->maxs[2] > DUCKING_MAX_Z && src->cast_info.move_evade && src->cast_info.currentmove != src->cast_info.move_evade
			&&	(src->cast_info.currentmove->endfunc != AI_CheckEvade))	// evade
		{

			src->cast_info.currentmove = src->cast_info.move_evade;
			src->cast_info.avoid_ent = dest;
			src->cast_info.last_avoid = level.time;

			if (src->cast_info.backoff)
				src->cast_info.backoff( src, dest );
		}

	}

	// if they just fired in our direction, attack
	else if	(	(dest->noise_time > (level.time - 1))
			 && (src->cast_info.currentmove->frame->aifunc == ai_stand)
			 &&	(dest->noise_type == PNOISE_WEAPON)
			 &&	(dest->client && dest->client->pers.weapon && (dest->client->pers.weapon->ammo))	// ignore if melee
			 &&	(cast_memory->memory_type != MEMORY_TYPE_FRIEND)
			 &&	(directly_infront_angle( dest->noise_angles, dest, src ))
			 &&	(AI_ClearSight(dest, src, false)))
	{
		AI_MakeEnemy( src, dest, 0 );
	}

	else if (	 (dest->client)
			 &&	!(dest->client->pers.holsteredweapon)
			 &&	 (cast_memory->memory_type != MEMORY_TYPE_FRIEND)
			 &&	 (dest->client->pers.weapon))
	{	// get mad, they have a weapon raised

		if (	(dist < AI_NOT_HOLSTERED_RANGE_1)
			&&	!(cast_memory->flags & MEMORY_HOSTILE_ENEMY))

		{	// always attack
			AI_MakeEnemy( src, dest, 0 );

			if (ai_debug_memory->value)
			{
				gi.dprintf("AI_RecordSighting: Weapon not holstered, attacking!\n");
			}
		}
		else if (dist < AI_NOT_HOLSTERED_RANGE_2)
		{	// let them know we're aware of them

			#define	TIME_TO_COMPLY	4.0

			if (!(cast_memory->flags & MEMORY_HOSTILE_ENEMY))
			{
				if (	(level.time > cast_memory->not_holstered_attack_time)
					&&	(level.time < (cast_memory->not_holstered_attack_time+5)))
				{	// attack, we've already warned them
					AI_MakeEnemy( src, dest, 0 );

					if (ai_debug_memory->value)
					{
						gi.dprintf("AI_RecordSighting: Weapon not holstered, attacking!\n");
					}
				}
				else if (level.time > cast_memory->not_holstered_attack_time)
				{
					if (	!(cast_memory->flags & MEMORY_WARNED_BACKOFF)
						&&	(infront(src, dest) && visible (src, dest)))
					{
						cast_memory->flags |= MEMORY_WARNED_BACKOFF;

						if (src->gender == GENDER_FEMALE)
						{
							extern voice_table_t f_backoff[];
							Voice_Random(src, src->cast_info.talk_ent, f_backoff, 3);
						}
						else
						{
							extern voice_table_t m_backoff[];

							// JOSEPH 26-MAY-99
							if (src->name_index == NAME_JESUS)
								Voice_Random(src, src->cast_info.talk_ent, &sr_jesus[6], 10);
							else if (src->name_index == NAME_KINGPIN)
								Voice_Random(src, src->cast_info.talk_ent, &kingpin[6], 10);
							else if (src->name_index == NAME_HEILMAN)
								Voice_Specific (src, src->cast_info.talk_ent, heilman, 11);
							else if (src->name_index == NAME_NICKIBLANCO)
								Voice_Specific (src, src->cast_info.talk_ent, nickiblanco, 11);
							else
								Voice_Random(src, src->cast_info.talk_ent, m_backoff, 3);
							// END JOSEPH
						}
					}
					// gi.sound( src, CHAN_VOICE, gi.soundindex( "actors/profanity/level2/cuss2-3.wav" ), 1, 2, 0 );

					// give them some "time to comply"
					cast_memory->not_holstered_attack_time = level.time + TIME_TO_COMPLY;
					
					// gi.dprintf("SOUND TODO: Drop your weapon biatch!\n");

					if (ai_debug_memory->value)
					{
						gi.dprintf("AI_RecordSighting: Weapon not holstered, you have %i seconds to comply\n", (int) TIME_TO_COMPLY);
					}

					// if we're standing around, turn to face them
					if (	(src->cast_info.currentmove->frame->aifunc == ai_stand)
						&&	(src->cast_info.move_avoid_walk && src->cast_info.move_avoid_crwalk))
					{
						src->cast_info.avoid( src, dest, true );
					}

				}
			}

			// if we're standing around, turn to face them
			if (src->cast_info.currentmove->frame->aifunc == ai_stand)
			{
				vec3_t vec;

				VectorSubtract( dest->s.origin, src->s.origin, vec );
				VectorNormalize( vec );
				src->ideal_yaw = vectoyaw( vec );

				M_ChangeYaw( src );
			}

			// evade?
			if (src->cast_info.currentmove->frame->aifunc == ai_stand && src->cast_info.move_stand_evade && (src->moral < MORAL_AGGRESSIVE))
			{
				src->cast_info.currentmove = src->cast_info.move_stand_evade;
				src->last_stand_evade = level.time;
			}

		}
		else if (dist < AI_NOT_HOLSTERED_RANGE_3)
		{	// if walking, ignore, if running, turn to face them, ready for attack if they get within range

			if (VectorLength( dest->velocity ) > 210)
			{	// running
				
				// if we're standing around, turn to face them
				if (src->cast_info.currentmove->frame->aifunc == ai_stand)
				{
					vec3_t vec;

					VectorSubtract( dest->s.origin, src->s.origin, vec );
					VectorNormalize( vec );
					src->ideal_yaw = vectoyaw( vec );

					M_ChangeYaw( src );
				}

			}

		}

	}

}
