1075 lines
24 KiB
C++
1075 lines
24 KiB
C++
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// $Logfile:: /EF2/Code/DLLs/game/actor_enemymanager.cpp $
|
||
|
// $Revision:: 41 $
|
||
|
// $Author:: Singlis $
|
||
|
// $Date:: 9/26/03 2:35p $
|
||
|
//
|
||
|
// Copyright (C) 2001 by Ritual Entertainment, Inc.
|
||
|
// All rights reserved.
|
||
|
//
|
||
|
// This source may not be distributed and/or modified without
|
||
|
// expressly written permission by Ritual Entertainment, Inc.
|
||
|
//
|
||
|
//
|
||
|
|
||
|
#include "_pch_cpp.h"
|
||
|
#include "actor_enemymanager.h"
|
||
|
#include "player.h"
|
||
|
#include "object.h"
|
||
|
|
||
|
//======================================
|
||
|
// EnemyManager Implementation
|
||
|
//=====================================
|
||
|
|
||
|
//
|
||
|
// Name: EnemyManager()
|
||
|
// Parameters: None
|
||
|
// Description: Constructor
|
||
|
//
|
||
|
EnemyManager::EnemyManager()
|
||
|
{
|
||
|
// Should always use other constructor
|
||
|
gi.Error( ERR_FATAL, "EnemyManager::EnemyManager -- Default Constructor Called" );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: EnemyManager()
|
||
|
// Parameters: Actor *actor
|
||
|
// Description: Constructor
|
||
|
//
|
||
|
EnemyManager::EnemyManager( Actor *actor )
|
||
|
{
|
||
|
//Initialize our Actor
|
||
|
if ( actor )
|
||
|
act = actor;
|
||
|
else
|
||
|
gi.Error( ERR_DROP, "EnemyManager::EnemyManager -- actor is NULL" );
|
||
|
|
||
|
_lockedOnCurrentEnemy = false;
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: ~EnemyManager()
|
||
|
// Parameters: None
|
||
|
// Description: Destructor
|
||
|
//
|
||
|
EnemyManager::~EnemyManager()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Name: GetCurrentEnemy()
|
||
|
// Parameters: None
|
||
|
// Description: Returns the _currentEnemy
|
||
|
//
|
||
|
EntityPtr EnemyManager::GetCurrentEnemy()
|
||
|
{
|
||
|
if ( act->forcedEnemy )
|
||
|
{
|
||
|
if ( act->forcedEnemy->health <= 0 )
|
||
|
{
|
||
|
act->forcedEnemy = NULL;
|
||
|
_lockedOnCurrentEnemy = false;
|
||
|
return _currentEnemy;
|
||
|
}
|
||
|
else
|
||
|
return act->forcedEnemy;
|
||
|
}
|
||
|
|
||
|
if ( !_currentEnemy )
|
||
|
FindHighestHateEnemy();
|
||
|
|
||
|
return _currentEnemy;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: SetCurrentEnemy()
|
||
|
// Parameters: Entity *enemy
|
||
|
// Description: sets _currentEnemy
|
||
|
//
|
||
|
void EnemyManager::SetCurrentEnemy( Entity *enemy )
|
||
|
{
|
||
|
if ( enemy )
|
||
|
_currentEnemy = enemy;
|
||
|
else
|
||
|
{
|
||
|
_lockedOnCurrentEnemy = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
EntityPtr EnemyManager::GetAlternateTarget()
|
||
|
{
|
||
|
return _alternateTarget;
|
||
|
}
|
||
|
|
||
|
void EnemyManager::SetAlternateTarget( Entity *target )
|
||
|
{
|
||
|
_alternateTarget = target;
|
||
|
}
|
||
|
//
|
||
|
// Name: FindHightestHateEnemy()
|
||
|
// Parameters: None
|
||
|
// Description: Sets the _currentEnemy to the highest enemy on the hate list
|
||
|
//
|
||
|
void EnemyManager::FindHighestHateEnemy()
|
||
|
{
|
||
|
float hateValue = 0;
|
||
|
HateListEntry_t listIndex;
|
||
|
float hateFactor;
|
||
|
|
||
|
|
||
|
|
||
|
if ( IsLockedOnCurrentEnemy() )
|
||
|
return;
|
||
|
|
||
|
|
||
|
for ( int i = _hateList.NumObjects() ; i > 0 ; i-- )
|
||
|
{
|
||
|
hateFactor = 100;
|
||
|
|
||
|
listIndex = _hateList.ObjectAt( i );
|
||
|
listIndex.hate = listIndex.hate * ( 1 / listIndex.lastDistance );
|
||
|
|
||
|
if ( !listIndex.enemy )
|
||
|
{
|
||
|
_hateList.RemoveObjectAt( i );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//If the enemy is a player, make sure to modify our hate
|
||
|
if ( listIndex.enemy->isSubclassOf( Sentient ) && !act->GetActorFlag(ACTOR_FLAG_UPDATE_HATE_WITH_ATTACKERS) )
|
||
|
{
|
||
|
Sentient *theEnemy;
|
||
|
theEnemy = (Sentient*)(Entity*)listIndex.enemy;
|
||
|
hateFactor *= theEnemy->GetHateModifier();
|
||
|
}
|
||
|
|
||
|
|
||
|
listIndex.hate *= hateFactor;
|
||
|
|
||
|
if ( listIndex.hate >= hateValue && listIndex.hate > 0.0 )
|
||
|
{
|
||
|
_currentEnemy = listIndex.enemy;
|
||
|
hateValue = listIndex.hate;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: FindClosestEnemy()
|
||
|
// Parameters: None
|
||
|
// Description: Sets the _currentEnemy to the enemy closest to the actor
|
||
|
//
|
||
|
void EnemyManager::FindClosestEnemy()
|
||
|
{
|
||
|
float distance = 99999999.9;
|
||
|
|
||
|
for ( int i = 1 ; i <= _hateList.NumObjects() ; i++ )
|
||
|
{
|
||
|
EntityPtr enemy;
|
||
|
HateListEntry_t listIndex;
|
||
|
|
||
|
listIndex = _hateList.ObjectAt( i );
|
||
|
|
||
|
if ( !act->combatSubsystem->CanAttackTarget( listIndex.enemy ) )
|
||
|
continue;
|
||
|
|
||
|
if ( listIndex.lastDistance < distance )
|
||
|
{
|
||
|
_currentEnemy = listIndex.enemy;
|
||
|
distance = listIndex.lastDistance;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void EnemyManager::FindNextEnemy()
|
||
|
{
|
||
|
HateListEntry_t listIndex;
|
||
|
float hateValue;
|
||
|
|
||
|
hateValue = 0;
|
||
|
for ( int i = 1 ; i <= _hateList.NumObjects() ; i++ )
|
||
|
{
|
||
|
listIndex = _hateList.ObjectAt( i );
|
||
|
if ( listIndex.hate <= _currentEnemyHate && listIndex.hate > hateValue )
|
||
|
{
|
||
|
hateValue = listIndex.hate;
|
||
|
_lastEnemy = _currentEnemy;
|
||
|
_currentEnemy = listIndex.enemy;
|
||
|
_currentEnemyHate = hateValue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: ClearCurrentEnemy()
|
||
|
// Parameters: None
|
||
|
// Description: sets the _currentEnemy to NULL
|
||
|
//
|
||
|
void EnemyManager::ClearCurrentEnemy()
|
||
|
{
|
||
|
_lockedOnCurrentEnemy = false;
|
||
|
_currentEnemy = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: ClearHateList()
|
||
|
// Parameters: None
|
||
|
// Description: Wipes out the hate list
|
||
|
//
|
||
|
void EnemyManager::ClearHateList()
|
||
|
{
|
||
|
for ( int i = _hateList.NumObjects() ; i > 0 ; i-- )
|
||
|
{
|
||
|
_hateList.RemoveObjectAt( i );
|
||
|
}
|
||
|
|
||
|
_hateList.FreeObjectList();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: Likes()
|
||
|
// Parameters: Entity *ent
|
||
|
// Description: Checks if Actor likes the entity
|
||
|
//
|
||
|
qboolean EnemyManager::Likes( Entity *ent )
|
||
|
{
|
||
|
Actor *actor;
|
||
|
|
||
|
if ( !ent )
|
||
|
return false;
|
||
|
|
||
|
if ( ent->isClient() )
|
||
|
{
|
||
|
return ( act->actortype == IS_FRIEND );
|
||
|
}
|
||
|
|
||
|
if ( act->actortype == IS_MONSTER )
|
||
|
{
|
||
|
// Monsters don't like anyone
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( ent->isSubclassOf( Actor ) )
|
||
|
{
|
||
|
actor = ( Actor * )ent;
|
||
|
|
||
|
if ( actor->enemytype == act->enemytype )
|
||
|
return true;
|
||
|
|
||
|
if ( actor->actortype != act->actortype )
|
||
|
return false;
|
||
|
else if ( act->actortype == IS_FRIEND )
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: Hates()
|
||
|
// Parameters: Entity *ent
|
||
|
// Description: Checks if Actor hates the entity
|
||
|
//
|
||
|
qboolean EnemyManager::Hates( Entity *ent )
|
||
|
{
|
||
|
Actor *actor;
|
||
|
|
||
|
if ( !ent )
|
||
|
return false;
|
||
|
|
||
|
if ( ent->isClient() )
|
||
|
{
|
||
|
return ( act->actortype != IS_CIVILIAN ) && (( act->actortype != IS_FRIEND ) && ( act->actortype != IS_TEAMMATE));
|
||
|
}
|
||
|
else if ( ent->isSubclassOf( Actor ) && ( act->actortype != IS_INANIMATE ) )
|
||
|
{
|
||
|
actor = ( Actor * )ent;
|
||
|
if ( ( actor->actortype <= IS_ENEMY ) && ( act->actortype <= IS_ENEMY ) )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
if ( ( actor->actortype == IS_FRIEND ) && ( act->actortype <= IS_ENEMY ) )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
if ( ( actor->actortype == IS_TEAMMATE ) && ( act->actortype <= IS_ENEMY ) )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
if ( ( actor->actortype <= IS_ENEMY ) && ( act->actortype == IS_FRIEND ) )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
if ( ( actor->actortype <= IS_ENEMY ) && ( act->actortype == IS_TEAMMATE ) )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
if ( ( actor->actortype == IS_TEAMMATE ) && ( act->actortype == IS_TEAMMATE ) )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: IsValidEnemy
|
||
|
// Parameters: Entity *enemy
|
||
|
// Description: Checks if the passed in entity is a valid target or not
|
||
|
//
|
||
|
qboolean EnemyManager::IsValidEnemy( Entity *enemy )
|
||
|
{
|
||
|
|
||
|
if ( !enemy || ( enemy == world ) || ( enemy == act ) || ( enemy->flags & FL_NOTARGET ) || ( enemy->takedamage == DAMAGE_NO ) || enemy->deadflag )
|
||
|
return false;
|
||
|
|
||
|
if ( !Hates( enemy ) )
|
||
|
return false;
|
||
|
|
||
|
if ( !CanAttack( enemy ) )
|
||
|
return false;
|
||
|
|
||
|
if ((enemy->edict->s.renderfx & RF_DONTDRAW) )
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: CanAttack
|
||
|
// Parameters: Entity *ent
|
||
|
// Description: Checks if the actor is allowed to attack the passed in entity
|
||
|
//
|
||
|
qboolean EnemyManager::CanAttack( Entity *ent )
|
||
|
{
|
||
|
|
||
|
if ( !ent )
|
||
|
return false;
|
||
|
|
||
|
if ( act->targetType == ATTACK_ANY )
|
||
|
return true;
|
||
|
|
||
|
|
||
|
if ( ent->isSubclassOf( Player ) && ( act->targetType == ATTACK_PLAYER_ONLY ) )
|
||
|
return true;
|
||
|
|
||
|
if ( ent->isSubclassOf( Actor ) && ( act->targetType == ATTACK_ACTORS_ONLY ) )
|
||
|
return true;
|
||
|
|
||
|
if ( ent->isSubclassOf ( LevelInteractionTrigger ) && ( act->targetType == ATTACK_LEVEL_INTERACTION ) )
|
||
|
return true;
|
||
|
|
||
|
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: CanAttackAnyEnemy
|
||
|
// Parameters: None
|
||
|
// Description: Checks if we can shoot any of our enemies in our list
|
||
|
qboolean EnemyManager::CanAttackAnyEnemy()
|
||
|
{
|
||
|
HateListEntry_t *listIndex;
|
||
|
Entity *target;
|
||
|
|
||
|
for ( int i = 1 ; i <= _hateList.NumObjects() ; i++ )
|
||
|
{
|
||
|
listIndex = &_hateList.ObjectAt( i );
|
||
|
|
||
|
target = listIndex->enemy;
|
||
|
if ( target )
|
||
|
{
|
||
|
if ( act->combatSubsystem->CanAttackTarget( target ) )
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: AdjustHate()
|
||
|
// Parameters: Entity *enemy -- The entity to adjust
|
||
|
// float adjustment -- how much to adjust
|
||
|
// Description: Adjusts the hate value of the enemy
|
||
|
//
|
||
|
void EnemyManager::AdjustHate( Entity *enemy , float adjustment )
|
||
|
{
|
||
|
HateListEntry_t *listIndex;
|
||
|
|
||
|
if ( !enemy )
|
||
|
return;
|
||
|
|
||
|
|
||
|
for ( int i = 1 ; i <= _hateList.NumObjects() ; i++ )
|
||
|
{
|
||
|
listIndex = &_hateList.ObjectAt( i );
|
||
|
|
||
|
if ( listIndex->enemy == enemy )
|
||
|
{
|
||
|
listIndex->hate += adjustment;
|
||
|
if ( listIndex->hate < 0.0f )
|
||
|
listIndex->hate = 0.0f;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: AdjustDamageCaused()
|
||
|
// Parameters: Entity *enemy -- The entity to adjust
|
||
|
// float adjustment -- how much to adjust
|
||
|
// Description: Adjusts the damage caused
|
||
|
//
|
||
|
void EnemyManager::AdjustDamageCaused( Entity *enemy , float adjustment )
|
||
|
{
|
||
|
HateListEntry_t listIndex;
|
||
|
|
||
|
if ( !enemy )
|
||
|
return;
|
||
|
|
||
|
for ( int i = 1 ; i <= _hateList.NumObjects() ; i++ )
|
||
|
{
|
||
|
listIndex = _hateList.ObjectAt( i );
|
||
|
|
||
|
if ( listIndex.enemy == enemy )
|
||
|
{
|
||
|
listIndex.damageCaused += adjustment;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: TryToAddToHateList
|
||
|
// Parameters: Entity *enemy
|
||
|
// Description: Checks if the passed in entity should be added to the hate list
|
||
|
// and calls _AddToHateList if it does
|
||
|
//
|
||
|
void EnemyManager::TryToAddToHateList( Entity *enemy )
|
||
|
{
|
||
|
if ( !enemy )
|
||
|
return;
|
||
|
|
||
|
if ( IsValidEnemy( enemy ) && !IsInHateList( enemy ) )
|
||
|
{
|
||
|
if ( !act->combatSubsystem->CanAttackTarget( enemy ) && !CanGetToEntity( enemy ) )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
_AddToHateList( enemy );
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: _AddToHateList()
|
||
|
// Parameters: Entity *enemy -- The enemy to add
|
||
|
// Description: Adds the enemy to the hate list
|
||
|
//
|
||
|
void EnemyManager::_AddToHateList( Entity *enemy )
|
||
|
{
|
||
|
HateListEntry_t listIndex;
|
||
|
|
||
|
if ( !enemy )
|
||
|
return;
|
||
|
|
||
|
listIndex.enemy = enemy;
|
||
|
listIndex.damageCaused = 0;
|
||
|
listIndex.hate = DEFAULT_INITIAL_HATE;
|
||
|
listIndex.lastSightTime = level.time ;
|
||
|
listIndex.canSee = false;
|
||
|
//listIndex.lastDistance = -1;
|
||
|
listIndex.nextSightTime = level.time + MIN_SIGHT_DELAY;
|
||
|
UpdateDistance( &listIndex );
|
||
|
_hateList.AddObject(listIndex);
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: IsInHateList()
|
||
|
// Parameters: Entity *enemy
|
||
|
// Description: Checks if enemy is in the hate list
|
||
|
//
|
||
|
qboolean EnemyManager::IsInHateList( Entity *enemy )
|
||
|
{
|
||
|
int index = _findEntityInHateList( enemy );
|
||
|
return index > 0;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------
|
||
|
// Name: IsLastInHateList
|
||
|
// Class: EnemyManager
|
||
|
//
|
||
|
// Description: Determine if the Entity passed is in the hate list,
|
||
|
// and if it is at the end of the list.
|
||
|
//
|
||
|
// Parameters: Entity* enemy - the entity to look for
|
||
|
//
|
||
|
// Returns: qboolean
|
||
|
//----------------------------------------------------------------
|
||
|
qboolean EnemyManager::IsLastInHateList( Entity* enemy )
|
||
|
{
|
||
|
int index = _findEntityInHateList( enemy );
|
||
|
return index == _hateList.NumObjects();
|
||
|
}
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------
|
||
|
// Name: _findEntityInHateList
|
||
|
// Class: EnemyManager
|
||
|
//
|
||
|
// Description: Search the hate list for the passed entity.
|
||
|
//
|
||
|
// Parameters: Entity* searchEnt - the entity to look for
|
||
|
//
|
||
|
// Returns: The index of the entity in the list
|
||
|
// 0 means it isn't in the list.
|
||
|
//----------------------------------------------------------------
|
||
|
int EnemyManager::_findEntityInHateList( Entity *searchEnt )
|
||
|
{
|
||
|
if( !searchEnt )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// manually iterate through the hatelist
|
||
|
HateListEntry_t listIndex;
|
||
|
for( int i = 1; i <= _hateList.NumObjects(); i++ )
|
||
|
{
|
||
|
listIndex = _hateList.ObjectAt( i );
|
||
|
|
||
|
if ( listIndex.enemy == searchEnt )
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// was not found, return 0
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------
|
||
|
// Name: TrivialUpdate
|
||
|
// Class: EnemyManager
|
||
|
//
|
||
|
// Description: Minimal Hatelist update required for Actors
|
||
|
//
|
||
|
// Parameters:
|
||
|
// None
|
||
|
//
|
||
|
// Returns: None
|
||
|
//----------------------------------------------------------------
|
||
|
void EnemyManager::TrivialUpdate()
|
||
|
{
|
||
|
for ( int i = _hateList.NumObjects() ; i > 0 ; i-- )
|
||
|
{
|
||
|
_hateList.ObjectAt( i ).lastDistance = 0.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: Update()
|
||
|
// Parameters: None
|
||
|
// Description: Updates HateListEntry_ts
|
||
|
//
|
||
|
void EnemyManager::Update()
|
||
|
{
|
||
|
HateListEntry_t *listIndex;
|
||
|
|
||
|
for ( int i = _hateList.NumObjects() ; i > 0 ; i-- )
|
||
|
{
|
||
|
listIndex = &_hateList.ObjectAt( i );
|
||
|
|
||
|
//Check if enemy is alive
|
||
|
if ( !act->IsEntityAlive( listIndex->enemy ) )
|
||
|
{
|
||
|
if ( listIndex->enemy == _currentEnemy )
|
||
|
ClearCurrentEnemy();
|
||
|
|
||
|
_hateList.RemoveObjectAt(i);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
UpdateDistance( listIndex );
|
||
|
UpdateCanSee( listIndex );
|
||
|
|
||
|
// Bump the player's action level, TODO: this needs to change to a general you have been targeted call sometime
|
||
|
|
||
|
if ( listIndex->enemy && listIndex->enemy->isSubclassOf( Player ) )
|
||
|
{
|
||
|
if ( act->GetActorFlag(ACTOR_FLAG_UPDATE_ACTION_LEVEL) )
|
||
|
{
|
||
|
Player *player = (Player *)(Entity *)listIndex->enemy;
|
||
|
player->IncreaseActionLevel( 100 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( act->GetActorFlag(ACTOR_FLAG_UPDATE_HATE_WITH_ATTACKERS) )
|
||
|
UpdateAttackers( listIndex );
|
||
|
|
||
|
/*if ( !act->combatSubsystem->CanAttackTarget( listIndex->enemy ) )
|
||
|
{
|
||
|
listIndex->hate = 0.0f;
|
||
|
if ( listIndex->enemy == _currentEnemy )
|
||
|
ClearCurrentEnemy();
|
||
|
|
||
|
_hateList.RemoveObjectAt(i);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
|
||
|
//If we haven't seen
|
||
|
if (
|
||
|
( act->timeBetweenSleepChecks > 0.0f ) &&
|
||
|
( listIndex->lastSightTime + act->timeBetweenSleepChecks <= level.time && !gi.inPVS( listIndex->enemy->centroid, act->centroid ) )
|
||
|
)
|
||
|
{
|
||
|
if ( listIndex->enemy == _currentEnemy && !_lockedOnCurrentEnemy )
|
||
|
ClearCurrentEnemy();
|
||
|
_hateList.RemoveObjectAt(i);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
// Try and go back to sleep
|
||
|
TrySleep();
|
||
|
|
||
|
|
||
|
//If we don't have an enemy, tell our senses to keep looking
|
||
|
act->sensoryPerception->SenseEnemies();
|
||
|
|
||
|
|
||
|
if ( GetCurrentEnemy() || act->GetActorFlag( ACTOR_FLAG_INVESTIGATING ) || ( act->mode == ACTOR_MODE_SCRIPT ) || ( act->mode == ACTOR_MODE_TALK ) )
|
||
|
act->last_time_active = level.time;
|
||
|
|
||
|
// Take care of enemies
|
||
|
act->strategos->Evaluate();
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: UpdateDistance
|
||
|
// Parameters: HateListEntry_t -- The List Entry to update
|
||
|
// Description: Updates the distance of the enemy
|
||
|
//
|
||
|
void EnemyManager::UpdateDistance( HateListEntry_t *listIndex )
|
||
|
{
|
||
|
Vector distance;
|
||
|
|
||
|
//float distanceLength;
|
||
|
|
||
|
if ( !listIndex || !listIndex->enemy )
|
||
|
return;
|
||
|
|
||
|
Vector enemyDistance;
|
||
|
enemyDistance = listIndex->enemy->origin;
|
||
|
|
||
|
distance = enemyDistance - act->origin;
|
||
|
//distanceLength = distance.length();
|
||
|
|
||
|
listIndex->lastDistance = distance.length();
|
||
|
|
||
|
}
|
||
|
|
||
|
void EnemyManager::UpdateAttackers( HateListEntry_t *listIndex )
|
||
|
{
|
||
|
if ( _currentEnemy == listIndex->enemy )
|
||
|
return;
|
||
|
|
||
|
int numberOfAttackers;
|
||
|
ActorGroup* group;
|
||
|
|
||
|
group = (ActorGroup*)groupcoordinator->GetGroup( act->GetGroupID() );
|
||
|
|
||
|
if ( !group )
|
||
|
return;
|
||
|
|
||
|
numberOfAttackers = group->CountMembersAttackingEnemy( listIndex->enemy );
|
||
|
|
||
|
listIndex->hate = listIndex->hate / (numberOfAttackers + 1);
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: UpdateCanSee
|
||
|
// Parameters: HateListEntry_t -- The List Entry to update
|
||
|
// Description: Updates the cansee of the enemy
|
||
|
//
|
||
|
void EnemyManager::UpdateCanSee( HateListEntry_t *listIndex )
|
||
|
{
|
||
|
if ( !listIndex || ( listIndex->nextSightTime > level.time ) )
|
||
|
return;
|
||
|
|
||
|
qboolean canSee;
|
||
|
canSee = act->sensoryPerception->CanSeeEntity( act , listIndex->enemy , true , true );
|
||
|
|
||
|
// Notify Strategos of status change
|
||
|
if ( canSee != listIndex->canSee )
|
||
|
act->strategos->NotifySightStatusChanged( listIndex->enemy , canSee );
|
||
|
|
||
|
// Update the SightCheckTimes
|
||
|
listIndex->canSee = canSee;
|
||
|
|
||
|
// We use a faster delay for our current enemy than everyone else
|
||
|
float next_sight_delay;
|
||
|
if ( listIndex->enemy == _currentEnemy )
|
||
|
next_sight_delay = act->max_inactive_time / 5.0f + G_CRandom( 0.5f );
|
||
|
else
|
||
|
next_sight_delay = MIN_SIGHT_DELAY + ( act->max_inactive_time / 2.0f + G_CRandom( 0.5f ) );
|
||
|
|
||
|
if ( next_sight_delay < 1.0f )
|
||
|
next_sight_delay = 1.0f;
|
||
|
|
||
|
listIndex->nextSightTime = level.time + next_sight_delay;
|
||
|
|
||
|
if ( canSee )
|
||
|
listIndex->lastSightTime = level.time;
|
||
|
|
||
|
if ( listIndex->lastSightTime + act->max_inactive_time < level.time )
|
||
|
ClearCurrentEnemy();
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Name: LockOnCurrentEnemy
|
||
|
// Parameters: qboolean lock
|
||
|
// Description: Sets the _lockedOnCurrentEnemy flag
|
||
|
//
|
||
|
void EnemyManager::LockOnCurrentEnemy( qboolean lock )
|
||
|
{
|
||
|
_lockedOnCurrentEnemy = lock;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: IsLockedOnCurrentEnemy
|
||
|
// Parameters: None
|
||
|
// Description: Returns the _lockedOnCurrentEnemy flag
|
||
|
//
|
||
|
qboolean EnemyManager::IsLockedOnCurrentEnemy()
|
||
|
{
|
||
|
return _lockedOnCurrentEnemy;
|
||
|
}
|
||
|
|
||
|
|
||
|
Vector EnemyManager::GetAwayFromEnemies()
|
||
|
{
|
||
|
EntityPtr enemy;
|
||
|
HateListEntry_t listIndex;
|
||
|
|
||
|
Vector enemyToSelf;
|
||
|
Vector temp;
|
||
|
float mag;
|
||
|
|
||
|
Vector bestAwayVector;
|
||
|
|
||
|
for ( int i = 1 ; i <= _hateList.NumObjects() ; i++ )
|
||
|
{
|
||
|
listIndex = _hateList.ObjectAt( i );
|
||
|
enemy = listIndex.enemy;
|
||
|
|
||
|
enemyToSelf = act->origin - enemy->origin;
|
||
|
mag = enemyToSelf.lengthSquared();
|
||
|
|
||
|
temp = enemyToSelf * (1 / mag);
|
||
|
|
||
|
bestAwayVector = bestAwayVector + temp;
|
||
|
}
|
||
|
|
||
|
bestAwayVector.normalize();
|
||
|
|
||
|
return bestAwayVector;
|
||
|
}
|
||
|
|
||
|
qboolean EnemyManager::InEnemyLineOfFire()
|
||
|
{
|
||
|
Actor *enemyActor = NULL;
|
||
|
Player *enemyPlayer = NULL;
|
||
|
|
||
|
Vector enemyToSelf;
|
||
|
|
||
|
float angleDiff = 0.0f;
|
||
|
|
||
|
|
||
|
if ( !_currentEnemy )
|
||
|
return false;
|
||
|
|
||
|
enemyToSelf = act->origin - _currentEnemy->origin;
|
||
|
enemyToSelf = enemyToSelf.toAngles();
|
||
|
|
||
|
if ( _currentEnemy->isSubclassOf(Actor) )
|
||
|
{
|
||
|
enemyActor = (Actor*)(Entity*)_currentEnemy;
|
||
|
|
||
|
if ( !enemyActor )
|
||
|
return false;
|
||
|
|
||
|
angleDiff = abs( (int) AngleNormalize180(enemyActor->angles[YAW] - enemyToSelf[YAW]) );
|
||
|
}
|
||
|
|
||
|
if ( _currentEnemy->isSubclassOf(Player) )
|
||
|
{
|
||
|
enemyPlayer = (Player*)(Entity*)_currentEnemy;
|
||
|
|
||
|
if ( !enemyPlayer )
|
||
|
return false;
|
||
|
|
||
|
Vector pAngles = enemyPlayer->GetVAngles();
|
||
|
|
||
|
angleDiff = abs( (int) AngleNormalize180(pAngles[YAW] - enemyToSelf[YAW]) );
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( angleDiff < 10.0f )
|
||
|
return true;
|
||
|
|
||
|
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
float EnemyManager::GetDistanceFromEnemy()
|
||
|
{
|
||
|
Vector selfToEnemy;
|
||
|
|
||
|
if ( !_currentEnemy )
|
||
|
return -1;
|
||
|
|
||
|
selfToEnemy = _currentEnemy->origin - act->origin;
|
||
|
|
||
|
return selfToEnemy.length();
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: DoArchive
|
||
|
// Parameters: Archiver &arc -- The archiver object
|
||
|
// Actor *actor -- The actor
|
||
|
// Description: Sets the act pointer and calls Archive
|
||
|
//
|
||
|
void EnemyManager::DoArchive( Archiver &arc , Actor *actor )
|
||
|
{
|
||
|
Archive( arc );
|
||
|
if ( actor )
|
||
|
act = actor;
|
||
|
else
|
||
|
gi.Error( ERR_FATAL, "EnemyManager::DoArchive -- actor is NULL" );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Name: Archive()
|
||
|
// Parameters: Archiver &arc -- The archiver object
|
||
|
// Description: Archives the class
|
||
|
//
|
||
|
void EnemyManager::Archive ( Archiver &arc )
|
||
|
{
|
||
|
HateListEntry_t hateEntry;
|
||
|
HateListEntry_t *realHateEntry;
|
||
|
int numEntries;
|
||
|
|
||
|
|
||
|
if ( arc.Saving() )
|
||
|
{
|
||
|
numEntries = _hateList.NumObjects();
|
||
|
arc.ArchiveInteger( &numEntries );
|
||
|
|
||
|
for ( int i = 1 ; i <= numEntries ; i++ )
|
||
|
{
|
||
|
hateEntry = _hateList.ObjectAt( i );
|
||
|
|
||
|
arc.ArchiveSafePointer( &hateEntry.enemy );
|
||
|
arc.ArchiveFloat( &hateEntry.lastSightTime );
|
||
|
arc.ArchiveFloat( &hateEntry.nextSightTime );
|
||
|
arc.ArchiveBoolean( &hateEntry.canSee );
|
||
|
arc.ArchiveFloat( &hateEntry.damageCaused );
|
||
|
arc.ArchiveFloat( &hateEntry.hate );
|
||
|
arc.ArchiveFloat( &hateEntry.lastDistance );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
arc.ArchiveInteger( &numEntries );
|
||
|
|
||
|
_hateList.Resize( numEntries );
|
||
|
|
||
|
for ( int i = 1 ; i <= numEntries ; i++ )
|
||
|
{
|
||
|
_hateList.AddObject( hateEntry );
|
||
|
|
||
|
realHateEntry = &_hateList.ObjectAt( i );
|
||
|
|
||
|
arc.ArchiveSafePointer( &realHateEntry->enemy );
|
||
|
arc.ArchiveFloat( &realHateEntry->lastSightTime );
|
||
|
arc.ArchiveFloat( &realHateEntry->nextSightTime );
|
||
|
arc.ArchiveBoolean( &realHateEntry->canSee );
|
||
|
arc.ArchiveFloat( &realHateEntry->damageCaused );
|
||
|
arc.ArchiveFloat( &realHateEntry->hate );
|
||
|
arc.ArchiveFloat( &realHateEntry->lastDistance );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
arc.ArchiveSafePointer( &_currentEnemy );
|
||
|
arc.ArchiveSafePointer( &_lastEnemy );
|
||
|
|
||
|
arc.ArchiveSafePointer( &_alternateTarget );
|
||
|
|
||
|
arc.ArchiveBoolean( &_lockedOnCurrentEnemy );
|
||
|
arc.ArchiveFloat( &_currentEnemyHate );
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------
|
||
|
// Name: TrySleep
|
||
|
// Class: EnemyManager
|
||
|
//
|
||
|
// Description: Puts the actor to sleep if it can
|
||
|
//
|
||
|
// Parameters:
|
||
|
// None
|
||
|
//
|
||
|
// Returns: None
|
||
|
//----------------------------------------------------------------
|
||
|
void EnemyManager::TrySleep( void )
|
||
|
{
|
||
|
const Entity *currentEnemy = GetCurrentEnemy();
|
||
|
// This is here because, if the ai gets turned off in the level, we don't want to go to sleep for if/when
|
||
|
// the ai gets turned back on in the level
|
||
|
if ( !level.ai_on )
|
||
|
act->last_time_active = level.time;
|
||
|
|
||
|
// See if we should go back to sleep -- If max_inactive_time < 1 , then the actor will never go to sleep
|
||
|
// If we DO have a max_inactive_time > 0 AND our last_time_active == 0 ( meaning it's our first time in here ) we
|
||
|
// should go ahead and try to fall asleep even though we have hit our max_inactive_time
|
||
|
if ( !currentEnemy && ( act->max_inactive_time > 0.0f ) && ( (act->last_time_active + act->max_inactive_time < level.time)))
|
||
|
{
|
||
|
//Well, we haven't had an enemy for a while, so let's see if the player is nearby
|
||
|
Player *player = 0;
|
||
|
for(int i = 0; i < game.maxclients; i++)
|
||
|
{
|
||
|
player = GetPlayer(i);
|
||
|
|
||
|
if ( player )
|
||
|
{
|
||
|
//We should NOT fall asleep if:
|
||
|
// our mode is ACTOR_MODE_IDLE _AND_ player flags are not equal to FL_NOTARGET
|
||
|
// OR
|
||
|
// Player is within our vision distance
|
||
|
// OR
|
||
|
// Player is within the PVS
|
||
|
|
||
|
//
|
||
|
// 7/15/02 -- SK
|
||
|
// Added via && the "gi.inPVS( player->centroid, act->centroid )" to the ACTOR_MODE_IDLE
|
||
|
// checks because, without it, all AI_OFF'd actors would still be considered "active"
|
||
|
// and this was ruining the framerate... I do not believe this is going to have any
|
||
|
// detrimental side effects.
|
||
|
//
|
||
|
if ( ( act->mode == ACTOR_MODE_IDLE ) && !(player->flags & FL_NOTARGET) && gi.inPVS( player->centroid, act->centroid ) || act->sensoryPerception->WithinVisionDistance(player) || gi.inPVS( player->centroid, act->centroid ) )
|
||
|
{
|
||
|
act->last_time_active = level.time;
|
||
|
}
|
||
|
|
||
|
else
|
||
|
{
|
||
|
if ( act->mode == ACTOR_MODE_AI )
|
||
|
{
|
||
|
act->EndMode();
|
||
|
|
||
|
act->Sleep();
|
||
|
|
||
|
if ( act->idle_thread.length() > 1 )
|
||
|
{
|
||
|
ExecuteThread( act->idle_thread, false );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
act->Sleep();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool EnemyManager::IsAnyEnemyInRange( float range )
|
||
|
{
|
||
|
HateListEntry_t *listIndex;
|
||
|
|
||
|
for ( int i = _hateList.NumObjects() ; i > 0 ; i-- )
|
||
|
{
|
||
|
listIndex = &_hateList.ObjectAt( i );
|
||
|
|
||
|
//Check if enemy is alive
|
||
|
if ( !act->IsEntityAlive( listIndex->enemy ) )
|
||
|
{
|
||
|
if ( listIndex->enemy == _currentEnemy )
|
||
|
ClearCurrentEnemy();
|
||
|
|
||
|
_hateList.RemoveObjectAt(i);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( act->WithinDistance( listIndex->enemy , range ) )
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
float EnemyManager::getEnemyCount()
|
||
|
{
|
||
|
return _hateList.NumObjects();
|
||
|
}
|
||
|
|
||
|
bool EnemyManager::CanGetToEntity(Entity *ent)
|
||
|
{
|
||
|
FindMovementPath find;
|
||
|
Path *path;
|
||
|
bool success;
|
||
|
|
||
|
success = false;
|
||
|
// Set up our pathing heuristics
|
||
|
find.heuristic.self = act;
|
||
|
find.heuristic.setSize( act->size );
|
||
|
find.heuristic.entnum = act->entnum;
|
||
|
|
||
|
path = find.FindPath( act->origin, ent->origin );
|
||
|
|
||
|
if ( path )
|
||
|
{
|
||
|
success = true;
|
||
|
}
|
||
|
|
||
|
delete path;
|
||
|
path = NULL;
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
bool EnemyManager::HasEnemy()
|
||
|
{
|
||
|
if ( _currentEnemy )
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|