/*
================

AI_Announcements.cpp

================
*/




#include "../Game_local.h"
#include "../Projectile.h"
#include "AI_Manager.h"

/*
=====================
idAI::AnnounceNewEnemy

Announce that we are going to shooting at a different enemy
=====================
*/
void idAI::AnnounceNewEnemy( void ) {
	if ( !CanAnnounce ( announceRate ) ) {
		return;
	}

	if ( !enemy.ent || !enemy.ent->IsType( idActor::GetClassType() ) ) {
		return;
	}

	if ( !aiManager.CheckTeamTimer ( team, AITEAMTIMER_ANNOUNCE_NEWENEMY ) ) {
		return;
	}

	// Check to see if we should announce the new enemy as a sniper
	// jshepard: Disabled by request
	/*
	if ( aiManager.CheckTeamTimer ( team, AITEAMTIMER_ANNOUNCE_SNIPER ) && enemy.ent->spawnArgs.GetBool ( "sniper" ) ) {
		// Should we announce the sniper as a high up sniper?
		if ( (enemy.lastKnownPosition - physicsObj.GetOrigin()) * physicsObj.GetGravityNormal() >= 250.0f ) {
			if ( Speak ( "lipsync_high_sniper", true ) ) {
				aiManager.SetTeamTimer ( team, AITEAMTIMER_ANNOUNCE_SNIPER, 10000 );
				return;
			}
			return;
		}
		
		// Just announce the sniper
		if ( Speak ( "lipsync_sniper", true ) ) {
			aiManager.SetTeamTimer ( team, AITEAMTIMER_ANNOUNCE_SNIPER, 10000 );
			return;
		}
	}
	*/

	idActor* newEnemyAct = static_cast<idActor*>(enemy.ent.GetEntity());
	bool	 result		 = false;

	//first see if the new enemy is behind our buddy closest to the enemy
	idActor* teammate = aiManager.NearestTeammateToPoint( this, newEnemyAct->GetPhysics()->GetOrigin(), false, 200.0f );
	if ( teammate ) {
		if ( aiManager.ActorIsBehindActor( newEnemyAct, teammate ) ) {
			result = Speak ( "lipsync_enemy_back", true );
		}
	}

	// If we havent spoken yet then find a teammat near us that we can tell about our enemy and
	// determine the best announcement to make based on their location
	if ( !result ) {
		teammate = aiManager.NearestTeammateToPoint( this, GetPhysics()->GetOrigin(), false, 300 );
		if ( teammate ) {
			if ( newEnemyAct->GetPhysics()->GetOrigin().z-GetPhysics()->GetOrigin().z >= 250.0f ) {
				result = Speak ( "lipsync_enemy_high", true );
			}
			
			// IF we still havent spoken and we're talking to the player, give him directional info
			if ( !result && teammate->IsType( idPlayer::GetClassType() ) ) {
				idPlayer* teamPlayer = static_cast<idPlayer*>(teammate);
				idVec3 dir = newEnemyAct->GetPhysics()->GetOrigin()-teamPlayer->GetPhysics()->GetOrigin();
				dir.z = 0;
				dir.NormalizeFast();
				idVec3 fwd = teamPlayer->viewAxis[0];
				fwd.z = 0.0f;
				fwd.NormalizeFast();
				idVec3 lt = teamPlayer->viewAxis[1];
				lt.z = 0.0f;
				lt.NormalizeFast();
				if ( fabs( dir * fwd ) < 0.4f )	{
					// more to the side than the front
					if ( dir * lt > 0 ) {
						result = Speak ( "lipsync_enemy_left", true );
					} else {
						result = Speak ( "lipsync_enemy_right", true );
					}
				}
			}
			
			// If we still havent spoken, just announce the enemy
			if ( !result ) {
				//FIXME: check right & left?
				//FIXME: check for surrounded?
				result = Speak ( "lipsync_enemy_default", true );
			}
		}
	}

	// If we spoke then set the timer
	if ( result ) {
		aiManager.SetTeamTimer ( team, AITEAMTIMER_ANNOUNCE_NEWENEMY, 2000 );
	}
}

/*
=====================
idAI::AnnounceKill

Announce that we have killed our enemy
=====================
*/
void idAI::AnnounceKill( idActor* victim ) {
	if ( !CanAnnounce ( announceRate ) ) {
		return;
	}

	// If already speaking or one the same team, dont announce the kill
	if ( victim->team == team ) {
		return;
	}
	//jshepard: "Watch It" sounds have been cut. Replaced with Victory.

	// Generic annoucement of enemy's death
	Speak( "lipsync_victory", true );


	return;

/*
	// If the victim is targetting the player and was close to the player when he died
	// then announce to the player to be careful
	if ( victim->IsType( idAI::Type ) )	{
		idAI* vicAI = static_cast<idAI*>(victim);
		if ( vicAI && vicAI->GetEnemy() && vicAI->GetEnemy()->IsType ( idPlayer::GetClassType() ) ) {
			idPlayer* vicEnemyPlayer = static_cast<idPlayer*>(vicAI->GetEnemy());
			if ( vicEnemyPlayer->team == team ) {
				idVec3 diff = vicAI->GetPhysics()->GetOrigin() - vicEnemyPlayer->GetPhysics()->GetOrigin();
				if ( !vicEnemyPlayer->CheckFOV ( vicAI->GetPhysics()->GetOrigin() ) || diff.LengthSqr() < 300.0f * 300.0f ) {
					Speak( "lipsync_watchit", true );
					return;
				}
			}
		}
	}
	
	// Chance that we say "watch it" to the closest teammate to him if the guy that died was facing the teammate 
	// when he died (ie, was a possible threat)
	idActor* teammate = aiManager.NearestTeammateToPoint( this, victim->GetPhysics()->GetOrigin(), false, 300.0f );
	if ( teammate && victim->CheckFOV(teammate->GetPhysics()->GetOrigin()) ) {		
		if ( gameLocal.random.RandomInt(2) < 1 ) {
			Speak( "lipsync_watchit", true );
			return;
		}
	}
*/

}

/*
=====================
idAI::AnnounceTactical

Announce the changing of tactical status
=====================
*/
void idAI::AnnounceTactical( aiTactical_t newTactical ) {
	bool result = false;
	
	// If already speaking dont bother
	if ( !CanAnnounce ( announceRate ) ) {
		return;
	}

	// Make sure nobody on this team has announced a tactical change recently
	if ( !aiManager.CheckTeamTimer ( team, AITEAMTIMER_ANNOUNCE_TACTICAL ) ) {
		return;
	}

	switch ( newTactical ) {
		case AITACTICAL_MELEE:
			if ( gameLocal.random.RandomFloat() < 0.2f ) {
				result = Speak( "lipsync_rush", true );
			}
			break;
/*
		case AITACTICAL_COVER:
			if ( gameLocal.random.RandomFloat() < 0.2f ) {
                result = Speak( "lipsync_cover", true );
			}
			break;
		case AITACTICAL_COVER_FLANK:
			result = Speak( "lipsync_flank", true );
			break;
		case AITACTICAL_COVER_ADVANCE:
			result = Speak( "lipsync_moveup", true );
			break;
		case AITACTICAL_COVER_RETREAT:
			result = Speak( "lipsync_fallback", true );
			break;
		case AITACTICAL_COVER_AMBUSH:
			break;
		case AITACTICAL_RANGED:
			break;
		case AITACTICAL_TURRET:
			break;
		case AITACTICAL_HIDE:
			result = Speak( "lipsync_cover", true );
			break;
*/
	}
	
	if ( result ) {
		aiManager.SetTeamTimer ( team, AITEAMTIMER_ANNOUNCE_TACTICAL, 2000 );
	}
}

/*
=====================
idAI::AnnounceSuppressed

Announce that someone is using supressing fire on us
=====================
*/
void idAI::AnnounceSuppressed( idActor *suppressor ) {

	//jshepard: Suppressed and suppressing removed by request
	return;
/*
	// Dont bother if we are already speaking
	if ( !CanAnnounce ( ) ) {
		return;
	}

	// Make sure nobody on this team has announced a tactical change recently
	if ( !aiManager.CheckTeamTimer ( team, AITEAMTIMER_ANNOUNCE_SUPPRESSED ) ) {
		return;
	}

	//FIXME: check crossfire?
	if ( Speak( "lipsync_supressed", true ) ) {
		aiManager.SetTeamTimer ( team, AITEAMTIMER_ANNOUNCE_SUPPRESSED, 3000 );
	}
*/

}

/*
=====================
idAI::AnnounceSuppressing

Announce that we are about to use supressing fire
=====================
*/
void idAI::AnnounceSuppressing( void ) {

	//jshepard: Suppressed and suppressing removed by request
	return;
/*
	// Dont bother if already speaking
	if ( !CanAnnounce ( ) ) {
		return;
	}

	// Make sure nobody on this team has announced a tactical change recently
	if ( !aiManager.CheckTeamTimer ( team, AITEAMTIMER_ANNOUNCE_SUPPRESSING ) ) {
		return;
	}

	//Make the guy being shot at know this
	if ( enemy.ent->IsType( idAI::Type ) ) {
		idAI* enemyAI = dynamic_cast<idAI*>(enemy.ent.GetEntity());
		if ( enemyAI  ) {
			enemyAI->AnnounceSuppressed( this );
		}
	}

	if ( Speak( "lipsync_supressing", true ) ) {
		aiManager.SetTeamTimer ( team, AITEAMTIMER_ANNOUNCE_SUPPRESSING, 5000 );
	}
*/
}

/*
=====================
idAI::AnnounceFlinch

Announce that an attack just missed us
=====================
*/
void idAI::AnnounceFlinch( idEntity *attacker ) {
	if ( !CanAnnounce ( announceRate ) ) {
		return;
	}

	idActor* attackActor = dynamic_cast<idActor*>(attacker);
	
	// Friendly fire?
	if ( attackActor && attackActor->team == team ) {
		if ( gameLocal.random.RandomFloat() < 0.2f ) {
			AnnounceFriendlyFire( attackActor );
		}
	} 
//jshepard: sniper announcement removed by request
/*
	else if ( attackActor->spawnArgs.GetBool ( "sniper" ) ) {
		//TEMP: static debounce timer
		static int lastPlayed2 = 0;
		if ( gameLocal.time - lastPlayed2 < 10000 ) {
			return;
		}
		lastPlayed2 = gameLocal.time;
		Speak( "lipsync_sniper", true );
	} else {
*/
	else {
		if ( gameLocal.random.RandomFloat() < 0.4f ) {
			return;
		}
		//TEMP: static debounce timer
		static int lastPlayed = 0;
		if ( gameLocal.time - lastPlayed < 5000 ) {
			return;
		}
		lastPlayed = gameLocal.time;
		Speak( "lipsync_closeone", true );
	}
}

/*
=====================
idAI::AnnounceInjured

Announce that we have been injured
=====================
*/
void idAI::AnnounceInjured( void ) {
	if ( !CanAnnounce ( 1.0f ) ) {
		return;
	}

	Speak( "lipsync_needhelp", true );
}

/*
=====================
idAI::AnnounceFriendlyFire

Announce that someone on our own team is shooting us
=====================
*/
void idAI::AnnounceFriendlyFire( idActor* attacker ) {
	if ( !CanAnnounce ( announceRate ) ) {
		return;
	}

	// Early outs
	if ( health <= 0 || attacker == this ) {
		return;
	}

	// Don't react to ff from other buddy AI
	if ( !attacker->IsType( idPlayer::GetClassType() ) ) {
		return;
	}

	// Make sure nobody on this team has announced a tactical change recently
	if ( !aiManager.CheckTeamTimer ( team, AITEAMTIMER_ANNOUNCE_FRIENDLYFIRE ) ) {
		return;
	}

	// Must be on same team for friendly fire
	if ( team != attacker->team ) {
		return;
	}
	
	// Must be close enough to hear it
	if ( DistanceTo ( attacker ) > 300.0f ) {
		return;
	}
	
	//FIXME: escalate?
	if ( Speak( "lipsync_checkfire", true ) ) {
		aiManager.SetTeamTimer ( team, AITEAMTIMER_ANNOUNCE_FRIENDLYFIRE, 1000 );
	}
}

/*
=====================
idAI::AnnounceGrenade
=====================
*/
void idAI::AnnounceGrenade( void ) {
	if ( !CanAnnounce ( 1 ) ) {
		return;
	}

	static int lastPlayed = 0;
	if ( gameLocal.time - lastPlayed < 5000 ) {
		return;
	}
	lastPlayed = gameLocal.time;
	//FIXME: escalate?
	Speak( "lipsync_grenade", true );
}

/*
=====================
idAI::AnnounceGrenadeThrow

Announce that we are throwing a grenade
=====================
*/
void idAI::AnnounceGrenadeThrow( void ) {
	if ( !CanAnnounce ( announceRate ) ) {
		return;
	}

	static int lastPlayed = 0;
	if ( gameLocal.time - lastPlayed < 1000 ) {
		return;
	}
	lastPlayed = gameLocal.time;
	//FIXME: escalate?
	Speak( "lipsync_throw_grenade", true );
}


/*
=====================
rvAIManager::AnnounceDeath

Announce through an ally of the victem that they have died
=====================
*/
void rvAIManager::AnnounceDeath( idAI* victim, idEntity* attacker ) {
	idActor* teammate;
	idAI*	 teammateAI;

	// Friendly fire kill?
	//MCG NOTE: This isn't even possible anymore...
	if ( attacker->IsType ( idPlayer::GetClassType() ) && static_cast<idPlayer*>(attacker)->team == victim->team ) {
		teammate   = NearestTeammateToPoint( static_cast<idActor*>(attacker), attacker->GetPhysics()->GetOrigin(), true, 500.0f );
		teammateAI = dynamic_cast<idAI*>(teammate);
		
		if ( teammateAI && teammateAI->CanAnnounce( teammateAI->announceRate ) ) {
			teammateAI->Speak( "lipsync_traitor", true );
			return;
		}
	}

	teammate   = NearestTeammateToPoint( victim, victim->GetPhysics()->GetOrigin(), true, 1000.0f );
	teammateAI = dynamic_cast<idAI*>(teammate);

	//jshepard: double check to make sure we don't call out our own death!
	if( teammateAI == victim )	{
		//MCG: note - NearestTeammateToPoint should *never* allow this, should never happen
		assert(0);
		return;
	}
	
	// Early out if we dont have a teammate or our teammate cant talk
	if ( !attacker || !teammateAI || !teammateAI->CanAnnounce ( teammateAI->announceRate ) ) {
		return;
	}
				
	// Announce sniper?
	// jshepard: sniper announcement removed by request
/*
	if ( attacker->spawnArgs.GetBool ( "sniper" ) ) {
		if ( !aiManager.CheckTeamTimer( teammateAI->team, AITEAMTIMER_ANNOUNCE_SNIPER ) ) {
			if ( teammateAI->Speak( "lipsync_sniper", true ) ) {
				aiManager.SetTeamTimer ( teammateAI->team, AITEAMTIMER_ANNOUNCE_SNIPER, 10000 );
				return;
			}
		} 
	}
*/
	// Annoucne specific death or just a generic death	
	const char* shortName;
	if ( !victim->spawnArgs.GetString ( "npc_shortname", "", &shortName ) || !*shortName ||
		 !teammateAI->Speak ( va("lipsync_%s_killed", shortName ), true ) ) {
		teammateAI->Speak( "lipsync_mandown", true );
	}
}

/*
=====================
idAI::AnnounceKill

Announces an ai being killed
=====================
*/
void rvAIManager::AnnounceKill ( idAI* victim, idEntity* attacker, idEntity* inflictor ) {
	idActor* teammate;
	idAI*	 teammateAI;
	
	// Friendly fire deaths are handled elsewhere
	if ( attacker->IsType ( idActor::GetClassType() ) && static_cast<idActor*>(attacker)->team == victim->team ) {
		return;
	}
	
	// If it was an AI guy that did the killing then just let him announce it
	if ( attacker->IsType( idAI::Type ) ) {
		//announce the kill
 		static_cast<idAI*>(attacker)->AnnounceKill( victim );
	} else if ( attacker->IsType( idPlayer::GetClassType() ) ) {
		idPlayer* attackerPlayer = static_cast<idPlayer*>(attacker);
		
		// If the guy who died is an AI guy who was targetting a buddy nearby, have him say "thanks!"
		// jshepard: these are cut unless we can get some tighter "thanks" quotes
/*		if ( victim->IsType( idAI::Type ) )	{
			idAI* victimAI = static_cast<idAI*>(victim);
			//if the victim's enemy is a teammate of mine, make the teammate say "thanks!"
			if ( victimAI && victimAI->GetEnemy() ) {
				idAI* victimEnemyAI = dynamic_cast<idAI*>(victimAI->GetEnemy());
				
				// See if the enemy of the guy who died is a teammate and wants to say thanks
				if ( victimEnemyAI && victimEnemyAI->CanAnnounce ( ) && victimEnemyAI->team == attackerPlayer->team ) {					
					float distSqr = (victimAI->GetPhysics()->GetOrigin() - victimEnemyAI->GetPhysics()->GetOrigin()).LengthSqr ( );
					if ( distSqr < Square ( 300.0f ) ) {
						//teammate was fighting him or close to him
						victimEnemyAI->Speak( "lipsync_thanks", true );
						return;
					}
				}
			}
		}	*/
		
		// Grab a nearby teammate of the player and say "nice shot!"
		teammate   = NearestTeammateToPoint( attackerPlayer, attacker->GetPhysics()->GetOrigin(), true, 500.0f, true );
		teammateAI = dynamic_cast<idAI*>(teammate);
		if ( teammateAI && teammateAI->CanAnnounce( teammateAI->announceRate ) ) {
			idProjectile* proj = dynamic_cast<idProjectile*>(inflictor);
			//killed them with a grenade?
			if ( proj && proj->spawnArgs.GetBool( "thrown" ) ) {
				teammateAI->Speak( "lipsync_nicetoss", true );
			// Or just shot them?
			} else {
				teammateAI->Speak( "lipsync_niceshot", true );
			}
			return;
		}
	}
}

