quake4-sdk/source/game/ai/AI_Util.cpp

978 lines
22 KiB
C++
Raw Permalink Normal View History

2007-06-15 00:00:00 +00:00
#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
#include "../spawner.h"
#include "AI_Manager.h"
#include "AI_Util.h"
#include "AAS_Find.h"
/*
===============================================================================
rvAIHelper
===============================================================================
*/
CLASS_DECLARATION( idEntity, rvAIHelper )
EVENT( EV_Activate, rvAIHelper::Event_Activate )
END_CLASS
/*
================
rvAIHelper::rvAIHelper
================
*/
rvAIHelper::rvAIHelper ( void ) {
helperNode.SetOwner( this );
}
/*
================
rvAIHelper::Spawn
================
*/
void rvAIHelper::Spawn ( void ) {
// Auto activate?
if ( spawnArgs.GetBool ( "start_on" ) ) {
PostEventMS ( &EV_Activate, 0, this );
}
}
/*
================
rvAIHelper::IsCombat
================
*/
bool rvAIHelper::IsCombat ( void ) const {
return false;
}
/*
================
rvAIHelper::OnActivate
================
*/
void rvAIHelper::OnActivate ( bool active ) {
if ( active ) {
ActivateTargets ( this );
}
}
/*
================
rvAIHelper::Event_Activate
================
*/
void rvAIHelper::Event_Activate( idEntity *activator ) {
if ( !helperNode.InList ( ) ) {
aiManager.RegisterHelper ( this );
OnActivate ( true );
} else {
aiManager.UnregisterHelper ( this );
OnActivate ( false );
}
}
/*
================
rvAIHelper::GetDirection
================
*/
idVec3 rvAIHelper::GetDirection ( const idAI* ai ) const {
if ( ai->team == 0 ) {
return GetPhysics()->GetAxis()[0];
}
return -GetPhysics()->GetAxis()[0];
}
/*
================
rvAIHelper::ValidateDestination
================
*/
bool rvAIHelper::ValidateDestination ( const idAI* ent, const idVec3& dest ) const {
return true;
}
/*
===============================================================================
rvAICombatHelper
===============================================================================
*/
class rvAICombatHelper : public rvAIHelper {
public:
CLASS_PROTOTYPE( rvAICombatHelper );
rvAICombatHelper ( void );
void Spawn ( void );
virtual bool IsCombat ( void ) const;
virtual bool ValidateDestination ( const idAI* ent, const idVec3& dest ) const;
protected:
virtual void OnActivate ( bool active );
idEntityPtr<idLocationEntity> location;
};
CLASS_DECLARATION( rvAIHelper, rvAICombatHelper )
END_CLASS
/*
================
rvAICombatHelper::rvAICombatHelper
================
*/
rvAICombatHelper::rvAICombatHelper ( void ) {
}
/*
================
rvAICombatHelper::Spawn
================
*/
void rvAICombatHelper::Spawn ( void ) {
}
/*
================
rvAICombatHelper::OnActivate
================
*/
void rvAICombatHelper::OnActivate ( bool active ) {
rvAIHelper::OnActivate ( active );
if ( active ) {
if ( spawnArgs.GetBool ( "tetherLocation", "1" ) ) {
location = gameLocal.LocationForPoint ( GetPhysics()->GetOrigin() );
} else {
location = NULL;
}
}
}
/*
================
rvAICombatHelper::IsCombat
================
*/
bool rvAICombatHelper::IsCombat ( void ) const {
return true;
}
/*
================
rvAICombatHelper::ValidateDestination
================
*/
bool rvAICombatHelper::ValidateDestination ( const idAI* ai, const idVec3& dest ) const {
// If tethering to a location then see if the location of the given points matches our tethered location
if ( location ) {
if ( gameLocal.LocationForPoint ( dest - ai->GetPhysics()->GetGravityNormal() * 32.0f ) != location ) {
return false;
}
}
// Is the destination on the wrong side of the helper?
idVec3 origin;
idVec3 dir;
dir = GetDirection(ai);
if ( ai->enemy.ent ) {
origin = ai->enemy.lastKnownPosition;
origin -= (dir * ai->combat.attackRange[0]);
} else {
origin = GetPhysics()->GetOrigin();
origin -= (dir * 32.0f);
}
if ( dir * (origin-dest) < 0.0f ) {
return false;
}
// Would this destination link us to the wrong helper?
if ( static_cast< const rvAIHelper * >( aiManager.FindClosestHelper( dest ) ) != this ) {
return false;
}
return true;
}
/*
===============================================================================
rvAIAvoid
===============================================================================
*/
class rvAIAvoid : public idEntity {
public:
CLASS_PROTOTYPE( rvAIAvoid );
rvAIAvoid ( void );
void Spawn ( void );
};
CLASS_DECLARATION( idEntity, rvAIAvoid )
END_CLASS
/*
================
rvAIAvoid::rvAIAvoid
================
*/
rvAIAvoid::rvAIAvoid ( void ) {
}
/*
================
rvAIAvoid::Spawn
================
*/
void rvAIAvoid::Spawn ( void ) {
int team = -1;
if ( !spawnArgs.GetInt ( "teamFilter", "-1", team ) ) {
//hmm, no "teamFilter" set, check "team" since many were set up like this
team = spawnArgs.GetInt ( "team", "-1" );
}
aiManager.AddAvoid ( GetPhysics()->GetOrigin(), spawnArgs.GetFloat ( "radius", "64" ), team );
PostEventMS ( &EV_Remove, 0 );
}
/*
===============================================================================
rvAITrigger
===============================================================================
*/
const idEventDef AI_AppendFromSpawner ( "<appendFromSpawner>", "ee" );
CLASS_DECLARATION( idEntity, rvAITrigger )
EVENT( EV_Activate, rvAITrigger::Event_Activate )
EVENT( AI_AppendFromSpawner, rvAITrigger::Event_AppendFromSpawner )
END_CLASS
/*
================
rvAITrigger::rvAITrigger
================
*/
rvAITrigger::rvAITrigger( void ) {
}
/*
================
rvAITrigger::Spawn
================
*/
void rvAITrigger::Spawn ( void ) {
nextTriggerTime = 0;
wait = SEC2MS ( spawnArgs.GetFloat ( "wait", "-1" ) );
conditionDead = spawnArgs.GetBool ( "condition_dead", "0" );
conditionTether = spawnArgs.GetBool ( "condition_tether", "0" );
conditionStop = spawnArgs.GetBool ( "condition_stop", "0" );
percent = spawnArgs.GetFloat ( "percent", "1" );
// Start on by default?
nextTriggerTime = spawnArgs.GetBool ( "start_on", "0" ) ? 0 : -1;
if ( nextTriggerTime == 0 ) {
BecomeActive ( TH_THINK );
} else {
BecomeInactive ( TH_THINK );
}
// If there are no conditions we are done
if ( !conditionDead && !conditionTether && !conditionStop ) {
gameLocal.Warning ( "No conditions specified on ai trigger entity '%s'", GetName ( ) );
PostEventMS ( &EV_Remove, 0 );
}
}
/*
================
rvAITrigger::Save
================
*/
void rvAITrigger::Save ( idSaveGame *savefile ) const {
int i;
savefile->WriteInt ( testAI.Num ( ) );
for ( i = 0; i < testAI.Num(); i ++ ) {
testAI[i].Save ( savefile );
}
savefile->WriteInt ( testSpawner.Num ( ) );
for ( i = 0; i < testSpawner.Num(); i ++ ) {
testSpawner[i].Save ( savefile );
}
savefile->WriteBool ( conditionDead );
savefile->WriteBool ( conditionTether );
savefile->WriteBool ( conditionStop );
savefile->WriteInt ( wait );
savefile->WriteInt ( nextTriggerTime );
savefile->WriteFloat ( percent );
}
/*
================
rvAITrigger::Restore
================
*/
void rvAITrigger::Restore ( idRestoreGame *savefile ) {
int i;
int num;
savefile->ReadInt ( num );
testAI.Clear ( );
testAI.SetNum ( num );
for ( i = 0; i < num; i ++ ) {
testAI[i].Restore ( savefile );
}
savefile->ReadInt ( num );
testSpawner.Clear ( );
testSpawner.SetNum ( num );
for ( i = 0; i < num; i ++ ) {
testSpawner[i].Restore ( savefile );
}
savefile->ReadBool ( conditionDead );
savefile->ReadBool ( conditionTether );
savefile->ReadBool ( conditionStop );
savefile->ReadInt ( wait );
savefile->ReadInt ( nextTriggerTime );
savefile->ReadFloat ( percent );
}
/*
================
rvAITrigger::Think
================
*/
void rvAITrigger::Think ( void ) {
int v;
// Only trigger so often
if ( nextTriggerTime == -1 || gameLocal.time < nextTriggerTime ) {
return;
}
// If we have any attached spawners then our condition cannot be met
if ( testSpawner.Num() ) {
for ( v = 0; v < testSpawner.Num(); v ++ ) {
rvSpawner* spawner = testSpawner[v];
if ( !spawner ) {
testSpawner.RemoveIndex ( v );
v--;
continue;
}
return;
}
}
// If we have no AI we are tracking then wait until we do
if ( !testAI.Num ( ) ) {
return;
}
int count = 0;
for ( v = 0; v < testAI.Num(); v ++ ) {
idAI* ai = testAI[v];
if ( !ai ) {
testAI.RemoveIndex ( v );
v--;
continue;
}
if ( !ai->aifl.dead ) {
if ( conditionDead ) {
continue;
}
if ( conditionTether && !ai->IsWithinTether ( ) ) {
continue;
}
if ( conditionStop && ai->move.fl.moving ) {
continue;
}
}
count++;
}
// If result is true then we should fire our trigger now
if ( count >= (int) ((float)testAI.Num()*percent) ) {
ActivateTargets ( this );
// If only triggering once just remove ourselves now
if ( wait < 0 ) {
nextTriggerTime = -1;
} else {
nextTriggerTime = gameLocal.time + wait;
}
}
}
/*
================
rvAITrigger::FindTargets
================
*/
void rvAITrigger::FindTargets ( void ) {
int t;
idEntity::FindTargets ( );
for ( t = 0; t < targets.Num(); t ++ ) {
idEntity* ent = targets[t];
if ( !ent ) {
continue;
}
if ( ent->IsType ( idAI::GetClassType ( ) ) ) {
testAI.Append ( idEntityPtr<idAI>(static_cast<idAI*>(ent)) );
targets.RemoveIndex ( t );
t--;
continue;
}
if ( ent->IsType ( rvSpawner::GetClassType ( ) ) ) {
static_cast<rvSpawner*>(ent)->AddCallback ( this, &AI_AppendFromSpawner );
testSpawner.Append ( idEntityPtr<rvSpawner>(static_cast<rvSpawner*>(ent)) );
targets.RemoveIndex ( t );
t--;
continue;
}
}
}
/*
================
rvAITrigger::Event_Activate
================
*/
void rvAITrigger::Event_Activate( idEntity *activator ) {
// Add spawners and ai to the list when they come in
if ( activator && activator->IsType ( idAI::GetClassType ( ) ) ) {
testAI.Append ( idEntityPtr<idAI>(static_cast<idAI*>(activator)) );
return;
}
if ( nextTriggerTime == -1 ) {
nextTriggerTime = 0;
BecomeActive ( TH_THINK );
} else {
nextTriggerTime = -1;
BecomeInactive ( TH_THINK );
}
}
/*
================
rvAITrigger::Event_AppendFromSpawner
================
*/
void rvAITrigger::Event_AppendFromSpawner ( rvSpawner* spawner, idEntity* spawned ) {
// If its an ai entity being spawned then add it to our test list
if ( spawned && spawned->IsType ( idAI::GetClassType ( ) ) ) {
testAI.Append ( idEntityPtr<idAI>(static_cast<idAI*>(spawned)) );
}
}
/*
===============================================================================
rvAITether
===============================================================================
*/
const idEventDef EV_TetherSetupLocation ( "tetherSetupLocation" );
const idEventDef EV_TetherGetLocation ( "tetherGetLocation" );
CLASS_DECLARATION( idEntity, rvAITether )
EVENT( EV_Activate, rvAITether::Event_Activate )
EVENT( EV_TetherGetLocation, rvAITether::Event_TetherGetLocation )
EVENT( EV_TetherSetupLocation, rvAITether::Event_TetherSetupLocation )
END_CLASS
/*
================
rvAITether::rvAITether
================
*/
rvAITether::rvAITether ( void ) {
}
/*
================
rvAITether::InitNonPersistentSpawnArgs
================
*/
void rvAITether::InitNonPersistentSpawnArgs ( void ) {
tfl.canBreak = spawnArgs.GetBool ( "allowBreak", "1" );
tfl.autoBreak = spawnArgs.GetBool ( "autoBreak", "0" );
tfl.forceRun = spawnArgs.GetBool ( "forceRun", "0" );
tfl.forceWalk = spawnArgs.GetBool ( "forceWalk", "0" );
tfl.becomeAggressive = spawnArgs.GetBool ( "becomeAggressive", "0" );
tfl.becomePassive = spawnArgs.GetBool ( "becomePassive", "0" );
// Check for both being set
if ( tfl.forceRun && tfl.forceWalk ) {
gameLocal.Warning ( "both forceRun and forceWalk were specified for tether '%s', forceRun will take precedence" );
tfl.forceWalk = false;
}
if ( tfl.becomeAggressive && tfl.becomePassive ) {
gameLocal.Warning ( "both becomePassive and becomeAggressive were specified for tether '%s', becomeAggressive will take precedence" );
tfl.becomePassive = false;
}
}
/*
================
rvAITether::Spawn
================
*/
void rvAITether::Spawn ( void ) {
InitNonPersistentSpawnArgs ( );
PostEventMS( &EV_TetherSetupLocation, 100 );
}
/*
================
rvAITether::Event_TetherSetupLocation
================
*/
void rvAITether::Event_TetherSetupLocation( void ) {
//NOTE: we now do this right after spawn so we don't stomp other tether's locations
// if we activate after them and are in the same room as them.
// Dynamically-spawned locations are very, very bad!
// All pre-existing locations should be placed and spread by now
// Get the location entity we are attached to
if ( spawnArgs.GetBool ( "location", "1" ) ) {
location = gameLocal.LocationForPoint ( GetPhysics()->GetOrigin() - GetPhysics()->GetGravityNormal() * 32.0f );
if ( !location ) {
location = gameLocal.AddLocation ( GetPhysics()->GetOrigin() - GetPhysics()->GetGravityNormal() * 32.0f, "tether_location" );
}
} else {
location = NULL;
}
PostEventMS( &EV_TetherGetLocation, 100 );
}
/*
================
rvAITether::Event_TetherGetLocation
================
*/
void rvAITether::Event_TetherGetLocation( void ) {
//NOW: all locations should be made & spread, get our location (may not be the one
// we added, it could be be the same as another tether if it's in the same room as us)
if ( spawnArgs.GetBool ( "location", "1" ) ) {
location = gameLocal.LocationForPoint ( GetPhysics()->GetOrigin() - GetPhysics()->GetGravityNormal() * 32.0f );
} else {
location = NULL;
}
}
/*
================
rvAITether::Save
================
*/
void rvAITether::Save ( idSaveGame *savefile ) const {
location.Save( savefile );
}
/*
================
rvAITether::Restore
================
*/
void rvAITether::Restore ( idRestoreGame *savefile ) {
location.Restore( savefile );
InitNonPersistentSpawnArgs ( );
}
/*
================
rvAITether::ValidateAAS
================
*/
bool rvAITether::ValidateAAS ( idAI* ai ) {
if ( !ai->aas ) {
return false;
}
return true;
}
/*
================
rvAITether::ValidateDestination
================
*/
bool rvAITether::ValidateDestination ( idAI* ai, const idVec3& dest ) {
if ( location ) {
if ( gameLocal.LocationForPoint ( dest - ai->GetPhysics()->GetGravityNormal() * 32.0f ) != location ) {
return false;
}
}
return true;
}
/*
================
rvAITether::ValidateBounds
================
*/
bool rvAITether::ValidateBounds ( const idBounds& bounds ) {
return true;
}
/*
================
rvAITether::FindGoal
================
*/
bool rvAITether::FindGoal ( idAI* ai, aasGoal_t& goal ) {
rvAASFindGoalForTether findGoal ( ai, this );
if ( !ai->aas->FindNearestGoal( goal,
ai->PointReachableAreaNum( ai->GetPhysics()->GetOrigin() ),
ai->GetPhysics()->GetOrigin(),
GetPhysics()->GetOrigin(),
ai->move.travelFlags,
0.0f, 0.0f,
NULL, 0, findGoal ) ) {
return false;
}
return true;
}
/*
================
rvAITether::Event_Activate
================
*/
void rvAITether::Event_Activate( idEntity *activator ) {
int i;
//WELL! Turns out designers are binding tethers to movers, so we have to get our location *again* on activation...
if ( spawnArgs.GetBool ( "location", "1" ) ) {
location = gameLocal.LocationForPoint ( GetPhysics()->GetOrigin() - GetPhysics()->GetGravityNormal() * 32.0f );
}
if ( activator && activator->IsType ( idAI::GetClassType() ) ) {
activator->ProcessEvent ( &EV_Activate, this );
}
// All targetted AI will be activated with the tether AI entity
for ( i = 0; i < targets.Num(); i ++ ) {
if ( !targets[i] ) {
continue;
}
if ( targets[i]->IsType ( idAI::GetClassType() ) ) {
targets[i]->ProcessEvent ( &EV_Activate, this );
// Aggressive/Passive stance change?
if ( tfl.becomeAggressive ) {
targets[i]->ProcessEvent ( &AI_BecomeAggressive );
} else if ( tfl.becomePassive ) {
targets[i]->ProcessEvent ( &AI_BecomePassive, false );
}
}
}
}
/*
================
rvAITether::DebugDraw
================
*/
void rvAITether::DebugDraw ( void ) {
const idBounds& bounds = GetPhysics()->GetAbsBounds();
gameRenderWorld->DebugBounds ( colorYellow, bounds.Expand ( 8.0f ) );
gameRenderWorld->DebugArrow ( colorWhite, bounds.GetCenter(), bounds.GetCenter() + GetPhysics()->GetAxis()[0] * 16.0f, 8.0f );
gameRenderWorld->DrawText( name.c_str(), bounds.GetCenter(), 0.1f, colorWhite, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
gameRenderWorld->DrawText( va( "#%d", entityNumber ), bounds.GetCenter() - GetPhysics()->GetGravityNormal() * 5.0f, 0.1f, colorWhite, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
}
/*
===============================================================================
rvAITetherBehind
===============================================================================
*/
CLASS_DECLARATION( rvAITether, rvAITetherBehind )
END_CLASS
/*
================
rvAITetherBehind::InitNonPersistentSpawnArgs
================
*/
void rvAITetherBehind::InitNonPersistentSpawnArgs ( void ) {
range = spawnArgs.GetFloat ( "range" );
}
/*
================
rvAITetherBehind::Spawn
================
*/
void rvAITetherBehind::Spawn ( void ) {
InitNonPersistentSpawnArgs ( );
}
/*
================
rvAITetherBehind::Restore
================
*/
void rvAITetherBehind::Restore ( idRestoreGame* savefile ) {
InitNonPersistentSpawnArgs ( );
}
/*
================
rvAITetherBehind::ValidateDestination
================
*/
bool rvAITetherBehind::ValidateDestination ( idAI* ai, const idVec3& dest ) {
// Check base tether first
if ( !rvAITether::ValidateDestination ( ai, dest ) ) {
return false;
}
// Make sure we include the move range in the tether
idVec3 origin;
if ( range ) {
origin = GetPhysics()->GetOrigin ( ) + GetPhysics()->GetAxis()[0] * GetOriginReachedRange() - GetPhysics()->GetAxis()[0] * range;
if ( GetPhysics()->GetAxis()[0] * (origin-dest) > 0.0f ) {
return false;
}
}
origin = GetPhysics()->GetOrigin ( ) - GetPhysics()->GetAxis()[0] * GetOriginReachedRange();
// Are we on wrong side of tether?
return ( GetPhysics()->GetAxis()[0] * (origin-dest) ) >= 0.0f;
}
/*
================
rvAITetherBehind::ValidateBounds
================
*/
bool rvAITetherBehind::ValidateBounds ( const idBounds& bounds ) {
if ( !rvAITether::ValidateBounds ( bounds ) ) {
return false;
}
idPlane plane;
int side;
plane.SetNormal ( GetPhysics()->GetAxis ( )[0] );
plane.FitThroughPoint ( GetPhysics()->GetOrigin ( ) );
side = bounds.PlaneSide ( plane );
return ( side == PLANESIDE_CROSS || side == PLANESIDE_BACK );
}
/*
================
rvAITetherBehind::DebugDraw
================
*/
void rvAITetherBehind::DebugDraw ( void ) {
idVec3 dir;
rvAITether::DebugDraw ( );
dir = GetPhysics()->GetGravityNormal ( ).Cross ( GetPhysics()->GetAxis()[0] );
gameRenderWorld->DebugLine ( colorPink,
GetPhysics()->GetOrigin() - dir * 1024.0f,
GetPhysics()->GetOrigin() + dir * 1024.0f );
if ( range ) {
idVec3 origin;
origin = GetPhysics()->GetOrigin ( ) - GetPhysics()->GetAxis ( )[0] * range;
gameRenderWorld->DebugArrow ( colorPink, GetPhysics()->GetOrigin(), origin, 5.0f );
gameRenderWorld->DebugLine ( colorPink,
origin - dir * 1024.0f,
origin + dir * 1024.0f );
}
}
/*
===============================================================================
rvAITetherRadius
===============================================================================
*/
CLASS_DECLARATION( rvAITether, rvAITetherRadius )
END_CLASS
/*
================
rvAITetherRadius::InitNonPersistentSpawnArgs
================
*/
void rvAITetherRadius::InitNonPersistentSpawnArgs ( void ) {
float radius;
if ( !spawnArgs.GetFloat ( "tetherRadius", "0", radius ) ) {
radius = spawnArgs.GetFloat ( "radius", "128" );
}
radiusSqr = Square ( radius );
}
/*
================
rvAITetherRadius::Spawn
================
*/
void rvAITetherRadius::Spawn ( void ) {
InitNonPersistentSpawnArgs ( );
}
/*
================
rvAITetherRadius::Restore
================
*/
void rvAITetherRadius::Restore ( idRestoreGame* savefile ) {
InitNonPersistentSpawnArgs ( );
}
/*
================
rvAITetherRadius::ValidateDestination
================
*/
bool rvAITetherRadius::ValidateDestination ( idAI* ai, const idVec3& dest ) {
// Check base tether first
if ( !rvAITether::ValidateDestination ( ai, dest ) ) {
return false;
}
// Are we within tether radius?
return ((dest - GetPhysics()->GetOrigin()).LengthSqr ( ) < radiusSqr - Square ( ai->move.range ) );
}
/*
================
rvAITetherRadius::ValidateBounds
================
*/
bool rvAITetherRadius::ValidateBounds ( const idBounds& bounds ) {
if ( !rvAITether::ValidateBounds ( bounds ) ) {
return false;
}
return ( Square ( bounds.ShortestDistance ( GetPhysics()->GetOrigin ( ) ) ) < radiusSqr );
}
/*
================
rvAITetherRadius::DebugDraw
================
*/
void rvAITetherRadius::DebugDraw ( void ) {
rvAITether::DebugDraw ( );
gameRenderWorld->DebugCircle( colorPink, GetPhysics()->GetOrigin(), GetPhysics()->GetGravityNormal(), idMath::Sqrt(radiusSqr), 25 );
}
/*
===============================================================================
rvAITetherClear
===============================================================================
*/
CLASS_DECLARATION( rvAITether, rvAITetherClear )
END_CLASS
/*
===============================================================================
rvAIBecomePassive
===============================================================================
*/
CLASS_DECLARATION( idEntity, rvAIBecomePassive )
EVENT( EV_Activate, rvAIBecomePassive::Event_Activate )
END_CLASS
/*
================
rvAIBecomePassive::Event_Activate
================
*/
void rvAIBecomePassive::Event_Activate( idEntity *activator ) {
int i;
bool ignoreEnemies;
ignoreEnemies = spawnArgs.GetBool ( "ignoreEnemies", "1" );
// All targeted AI will become passive
for ( i = 0; i < targets.Num(); i ++ ) {
if ( !targets[i] ) {
continue;
}
if ( targets[i]->IsType ( idAI::GetClassType() ) ) {
targets[i]->ProcessEvent ( &AI_BecomePassive, ignoreEnemies );
}
}
}
/*
===============================================================================
rvAIBecomeAggressive
===============================================================================
*/
CLASS_DECLARATION( idEntity, rvAIBecomeAggressive )
EVENT( EV_Activate, rvAIBecomeAggressive::Event_Activate )
END_CLASS
/*
================
rvAIBecomeAggressive::Event_Activate
================
*/
void rvAIBecomeAggressive::Event_Activate( idEntity *activator ) {
int i;
// All targetted AI will become aggressive
for ( i = 0; i < targets.Num(); i ++ ) {
if ( !targets[i] ) {
continue;
}
if ( targets[i]->IsType ( idAI::GetClassType() ) ) {
targets[i]->ProcessEvent ( &AI_BecomeAggressive );
}
}
}