quake4-sdk/source/mpgame/ai/AI_Announcements.cpp
2007-06-15 00:00:00 +00:00

554 lines
15 KiB
C++

/*
================
AI_Announcements.cpp
================
*/
#include "../../idlib/precompiled.h"
#pragma hdrstop
#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;
}
}
}