1085 lines
26 KiB
C++
1085 lines
26 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /Code/DLLs/game/actor_sensoryperception.cpp $
|
|
// $Revision:: 21 $
|
|
// $Author:: Sketcher $
|
|
// $Date:: 4/09/03 5:04p $
|
|
//
|
|
// 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_sensoryperception.h"
|
|
#include "player.h"
|
|
#include "object.h"
|
|
|
|
//======================================
|
|
// SensoryPerception Implementation
|
|
//======================================
|
|
|
|
//
|
|
// Name: SensoryPerception()
|
|
// Parameters: None
|
|
// Description: Constructor
|
|
//
|
|
SensoryPerception::SensoryPerception()
|
|
{
|
|
// Should always use other constructor
|
|
gi.Error( ERR_FATAL, "SensoryPerception::SensoryPerception -- Default Constructor Called" );
|
|
}
|
|
|
|
//
|
|
// Name: SensoryPerception()
|
|
// Parameters: Actor* actor -- The actor pointer to be assigned
|
|
// to the internal actor pointer
|
|
// Description: Constructor
|
|
//
|
|
SensoryPerception::SensoryPerception(Actor *actor )
|
|
{
|
|
//Initialize our Actor
|
|
if ( actor )
|
|
act = actor;
|
|
else
|
|
gi.Error( ERR_FATAL, "SensoryPerception::SensoryPerception -- actor is NULL" );
|
|
|
|
_init();
|
|
}
|
|
|
|
//
|
|
// Name: ~SensoryPerception()
|
|
// Parameters: None
|
|
// Description: Destructor
|
|
//
|
|
SensoryPerception::~SensoryPerception()
|
|
{
|
|
|
|
}
|
|
|
|
qboolean SensoryPerception::isInLineOfSight( const Vector &position , const int entNum )
|
|
{
|
|
trace_t trace;
|
|
Vector startPos;
|
|
Vector endPos;
|
|
Entity *traceEnt;
|
|
|
|
|
|
startPos = act->origin;
|
|
startPos.z += 15;
|
|
|
|
endPos = position;
|
|
endPos.z += 15;
|
|
|
|
Vector modMins;
|
|
Vector modMaxs;
|
|
|
|
modMins = act->mins;
|
|
modMins *= 1.5;
|
|
|
|
modMaxs = act->maxs;
|
|
modMaxs *= 1.5;
|
|
|
|
trace = G_Trace( startPos, modMins, modMaxs, endPos, act, act->edict->clipmask, false, "isInLineOfSight" );
|
|
//G_DebugLine( startPos , trace.endpos, 1.0f, 0.0f, 1.0f, 1.0f );
|
|
|
|
_lineOfSight.entNum = entNum;
|
|
_lineOfSight.time = level.time;
|
|
|
|
if ( trace.entityNum == entNum || entNum == ENTITYNUM_NONE )
|
|
{
|
|
_lineOfSight.inLineOfSight = true;
|
|
}
|
|
else
|
|
{
|
|
traceEnt = G_GetEntity( trace.entityNum );
|
|
_lineOfSight.inLineOfSight = false;
|
|
|
|
if ( traceEnt && traceEnt->isSubclassOf( Actor) )
|
|
{
|
|
Actor *traceActor;
|
|
traceActor = (Actor*)traceEnt;
|
|
|
|
_lineOfSight.inLineOfSight = traceActor->sensoryPerception->checkInLineOfSight( position , entNum );
|
|
}
|
|
}
|
|
|
|
return _lineOfSight.inLineOfSight;
|
|
}
|
|
|
|
qboolean SensoryPerception::checkInLineOfSight( const Vector &position , const int entNum )
|
|
{
|
|
float timeDelta;
|
|
|
|
timeDelta = level.time - _lineOfSight.time;
|
|
|
|
if ( timeDelta > .5 || entNum != _lineOfSight.entNum )
|
|
return isInLineOfSight( position , entNum );
|
|
else
|
|
return _lineOfSight.inLineOfSight;
|
|
|
|
}
|
|
|
|
void SensoryPerception::_init()
|
|
{
|
|
|
|
// Set up Stimuli
|
|
_stimuli = STIMULI_ALL;
|
|
_permanent_stimuli = STIMULI_ALL;
|
|
|
|
//Member Vars
|
|
_noise_position = vec_zero;
|
|
_noise_time = 0;
|
|
_fov = DEFAULT_FOV;
|
|
_fovdot = (float)cos( (double)_fov * 0.5 * M_PI / 180.0 );
|
|
_vision_distance = DEFAULT_VISION_DISTANCE;
|
|
_last_soundType = SOUNDTYPE_NONE;
|
|
_nextSenseTime = 0.0f;
|
|
|
|
_lineOfSight.entNum = 0;
|
|
_lineOfSight.inLineOfSight = false;
|
|
_lineOfSight.time = 0.0f;
|
|
}
|
|
|
|
//
|
|
// Name: SenseEnemies()
|
|
// Parameters: None
|
|
// Description: Checks for players and teammates, to see if the actor
|
|
// needs to wake up
|
|
//
|
|
void SensoryPerception::SenseEnemies()
|
|
{
|
|
int i;
|
|
|
|
if ( _nextSenseTime > level.time )
|
|
return;
|
|
|
|
_nextSenseTime = level.time + 0.5f + G_Random( 0.2f );
|
|
//_nextSenseTime = 0;
|
|
|
|
//Well, who your enemies are depends on what side your on
|
|
if ( ( act->actortype == IS_ENEMY ) || ( act->actortype == IS_MONSTER ) )
|
|
{
|
|
//
|
|
//If we an ENEMY or a MONSTER than teammates are our enemies
|
|
//
|
|
Sentient *teammate;
|
|
for ( i = 1 ; i <= TeamMateList.NumObjects() ; i++ )
|
|
{
|
|
teammate = TeamMateList.ObjectAt( i );
|
|
|
|
if ( _SenseEntity( teammate ) ) return;
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//If we an CIVILIAN, FRIEND, or TEAMMATE our potiential enemies are active monsters
|
|
//
|
|
Entity *ent;
|
|
|
|
for ( i = 1 ; i <= ActiveList.NumObjects() ; i++ )
|
|
{
|
|
ent = ActiveList.ObjectAt( i );
|
|
_SenseEntity(ent);
|
|
}
|
|
|
|
//In case we didn't find an enemy, but if the players nearby, we want to wake up anyway
|
|
_SenseEntity( GetPlayer( 0 ) );
|
|
}
|
|
|
|
}
|
|
|
|
qboolean SensoryPerception::_SenseEntity( Entity *ent )
|
|
{
|
|
// Dont want to target the enemy if he's not a valid target
|
|
|
|
if (!ent)
|
|
return false;
|
|
|
|
if ( !EntityIsValidTarget(ent) )
|
|
return false;
|
|
|
|
// Dont wake ourselves up
|
|
if ( ent->entnum == act->entnum )
|
|
return false;
|
|
|
|
if ( ent->isSubclassOf ( Actor ) )
|
|
{
|
|
if ( !ent->isThinkOn() )
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
// Check if we're in the PVS
|
|
if ( gi.inPVS( ent->centroid, act->centroid ) )
|
|
{
|
|
// Check if we can see the enemy
|
|
if ( CanSeeEntity ( act , ent , true , true ) )
|
|
Stimuli( STIMULI_SIGHT , ent );
|
|
else
|
|
{
|
|
// Lets not idle for teammates, just players
|
|
if ( ent->isSubclassOf( Player ) )
|
|
{
|
|
// We couldn't see the enemy, but we're in the PVS, so we need to wake up and idle a bit.
|
|
if ( ( world->farplane_distance == 0 ) || ( Distance( ent->centroid, act->centroid ) < world->farplane_distance ) )
|
|
{
|
|
act->Wakeup();
|
|
//act->ActivateAI();
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
if ( gi.inPVS( ent->centroid, act->centroid ) )
|
|
{
|
|
Vector enemyToSelf;
|
|
enemyToSelf = act->origin - ent->origin;
|
|
|
|
float visionDistance = GetVisionDistance();
|
|
if ( enemyToSelf.length() < visionDistance )
|
|
{
|
|
if ( CanSeeEntity( act, ent, true, true ) )
|
|
Stimuli( STIMULI_SIGHT , ent );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Name: Stimuli()
|
|
// Parameters: int stimuli -- The Stimuli
|
|
// Description: Wakes up the actor if it should respond to the stimuli
|
|
//
|
|
void SensoryPerception::Stimuli( int stimuli )
|
|
{
|
|
if ( ShouldRespondToStimuli( stimuli ) )
|
|
{
|
|
act->Wakeup();
|
|
act->ActivateAI();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Name: Stimuli()
|
|
// Parameters: int new_stimuli -- The Stimuli
|
|
// Entity ent -- The Entity
|
|
// qboolean force -- Force the actor to make the entity an enemy
|
|
// Description: Wakes up the actor if it should respond to the stimuli
|
|
//
|
|
void SensoryPerception::Stimuli( int new_stimuli, Entity *ent )
|
|
{
|
|
if ( !ent )
|
|
return;
|
|
|
|
if ( ShouldRespondToStimuli( new_stimuli ) && EntityIsValidTarget(ent))
|
|
{
|
|
act->enemyManager->TryToAddToHateList( ent );
|
|
act->Wakeup();
|
|
act->ActivateAI();
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Name: Stimuli()
|
|
// Parameters: int new_stimuli -- The Stimuli
|
|
// const Vector &pos -- The location of the stimuli
|
|
// Description: Wakes up the actor if it should respond to the stimuli
|
|
//
|
|
void SensoryPerception::Stimuli( int new_stimuli, const Vector &pos )
|
|
{
|
|
|
|
if ( ShouldRespondToStimuli( new_stimuli ) )
|
|
{
|
|
// Investigate the position
|
|
|
|
if ( !act->GetActorFlag( ACTOR_FLAG_NOISE_HEARD ) )
|
|
{
|
|
_noise_position = pos;
|
|
_noise_time = level.time;
|
|
act->SetActorFlag( ACTOR_FLAG_NOISE_HEARD, true );
|
|
}
|
|
|
|
if ( act->last_time_active + 10.0f < level.time )
|
|
{
|
|
act->Wakeup();
|
|
act->ActivateAI();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Name: Stimuli()
|
|
// Parameters: int new_stimuli -- The stimuli
|
|
// const Vector &pos -- The location of the stimuli
|
|
// int sound_Type -- sound type of the stimuli
|
|
// Description: Wakes up the actor if it should respond to the stimuli
|
|
//
|
|
void SensoryPerception::Stimuli( int new_stimuli, const Vector &pos, int sound_Type )
|
|
{
|
|
|
|
if ( ShouldRespondToStimuli( new_stimuli ) )
|
|
{
|
|
|
|
if ( !act->GetActorFlag( ACTOR_FLAG_NOISE_HEARD ) )
|
|
{
|
|
_noise_position = pos;
|
|
_last_soundType = sound_Type;
|
|
_noise_time = level.time;
|
|
act->SetActorFlag( ACTOR_FLAG_NOISE_HEARD, true );
|
|
}
|
|
|
|
if ( act->last_time_active + 10.0f < level.time )
|
|
{
|
|
act->Wakeup();
|
|
act->ActivateAI();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Name: RespondTo()
|
|
// Parameters: const str &stimuli_name -- string name for the stimuli
|
|
// qboolean respond -- Respond or Not to the stimuli
|
|
// Description: sets if the actor will respond to a particular stimulus
|
|
//
|
|
void SensoryPerception::RespondTo(const str &stimuli_name , qboolean respond )
|
|
{
|
|
if ( !Q_stricmp( stimuli_name.c_str() , "sight" ) )
|
|
RespondTo(STIMULI_SIGHT , respond );
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "sound" ) )
|
|
RespondTo(STIMULI_SOUND , respond );
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "pain" ) )
|
|
RespondTo(STIMULI_PAIN , respond );
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "script" ) )
|
|
RespondTo(STIMULI_SCRIPT , respond );
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "all" ) )
|
|
RespondTo(STIMULI_ALL , respond );
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "none" ) )
|
|
RespondTo(STIMULI_NONE , respond );
|
|
}
|
|
|
|
//
|
|
// Name: RespondTo()
|
|
// Parameters: int stimuli -- the stimuli
|
|
// qboolean respond -- Respond or Not to the stimuli
|
|
// Description: sets if the actor will respond to a particular stimulus
|
|
//
|
|
void SensoryPerception::RespondTo( int stimuli , qboolean respond )
|
|
{
|
|
if ( stimuli == STIMULI_ALL )
|
|
{
|
|
if ( respond )
|
|
_stimuli = STIMULI_ALL;
|
|
else
|
|
_stimuli = STIMULI_NONE;
|
|
|
|
return;
|
|
}
|
|
|
|
if ( stimuli == STIMULI_NONE )
|
|
{
|
|
if ( respond )
|
|
_stimuli = STIMULI_NONE;
|
|
else
|
|
_stimuli = STIMULI_ALL;
|
|
|
|
return;
|
|
}
|
|
|
|
if ( respond )
|
|
_stimuli |= stimuli;
|
|
else
|
|
_stimuli &= ~stimuli;
|
|
}
|
|
|
|
//
|
|
// Name: PermanentlyRespondTo
|
|
// Parameters: const str &stimuli_name -- string name for the stimuli
|
|
// qboolean respond -- Respond or Not to the stimuli
|
|
// Description: Allows the actor to ALWAYS respond or not to a particular stimulus
|
|
// this allows us to make actors that are deaf or blind... etc
|
|
//
|
|
void SensoryPerception::PermanentlyRespondTo(const str &stimuli_name , qboolean respond )
|
|
{
|
|
if ( !Q_stricmp( stimuli_name.c_str() , "sight" ) )
|
|
{
|
|
if ( respond )
|
|
_permanent_stimuli |= STIMULI_SIGHT;
|
|
else
|
|
_permanent_stimuli &= ~STIMULI_SIGHT;
|
|
}
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "sound" ) )
|
|
{
|
|
if ( respond )
|
|
_permanent_stimuli |= STIMULI_SOUND;
|
|
else
|
|
_permanent_stimuli &= ~STIMULI_SOUND;
|
|
}
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "pain" ) )
|
|
{
|
|
if ( respond )
|
|
_permanent_stimuli |= STIMULI_PAIN;
|
|
else
|
|
_permanent_stimuli &= ~STIMULI_PAIN;
|
|
|
|
}
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "script" ) )
|
|
{
|
|
if ( respond )
|
|
_permanent_stimuli |= STIMULI_SCRIPT;
|
|
else
|
|
_permanent_stimuli &= ~STIMULI_SCRIPT;
|
|
|
|
}
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "all" ) )
|
|
{
|
|
if ( respond )
|
|
_permanent_stimuli = STIMULI_ALL;
|
|
else
|
|
_permanent_stimuli = STIMULI_NONE;
|
|
|
|
}
|
|
else if ( !Q_stricmp( stimuli_name.c_str() , "none" ) )
|
|
{
|
|
if ( respond )
|
|
_permanent_stimuli = STIMULI_NONE;
|
|
else
|
|
_permanent_stimuli = STIMULI_ALL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Name: ShouldRespondToStimuli()
|
|
// Parameters: int new_stimuli -- the stimuli to check
|
|
// Description: Checks if the actor should respond to the stimuli
|
|
//
|
|
qboolean SensoryPerception::ShouldRespondToStimuli( int new_stimuli )
|
|
{
|
|
if( ( act->targetType == ATTACK_SCRIPTED_ONLY ) && ( new_stimuli != STIMULI_SCRIPT ) ) return false;
|
|
|
|
if ( _stimuli == STIMULI_ALL )
|
|
return true;
|
|
|
|
if ( _stimuli == STIMULI_NONE )
|
|
return false;
|
|
|
|
return ( (( new_stimuli & _stimuli ) && ( new_stimuli & _permanent_stimuli )) || ( new_stimuli == STIMULI_SCRIPT ) );
|
|
}
|
|
|
|
|
|
//
|
|
// Name: ShowInfo()
|
|
// Parameters: None
|
|
// Description: Prints sensoryinformation to the console
|
|
//
|
|
void SensoryPerception::ShowInfo()
|
|
{
|
|
if ( ShouldRespondToStimuli( STIMULI_ALL ) )
|
|
{
|
|
gi.Printf( "Actor is Responding To: ALL" );
|
|
return;
|
|
}
|
|
|
|
if ( ShouldRespondToStimuli( STIMULI_NONE ) )
|
|
{
|
|
gi.Printf( "Actor is Responding To: NONE" );
|
|
return;
|
|
}
|
|
|
|
if ( ShouldRespondToStimuli( STIMULI_SIGHT ) )
|
|
gi.Printf( "Actor is Responding To: SIGHT" );
|
|
|
|
if ( ShouldRespondToStimuli( STIMULI_SOUND ) )
|
|
gi.Printf( "Actor is Responding To: SOUND" );
|
|
|
|
if ( ShouldRespondToStimuli( STIMULI_PAIN ) )
|
|
gi.Printf( "Actor is Responding To: PAIN" );
|
|
|
|
if ( ShouldRespondToStimuli( STIMULI_SCRIPT ) )
|
|
gi.Printf( "Actor is Responding To: SCRIPT" );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Name: WithinVisionDistance()
|
|
// Parameters: Entity *ent -- The entity to be checked
|
|
// Description: Checks if the passed in entity is
|
|
// within the vision_distance of the actor, or the
|
|
// farplane_distance of the world ( whichever is smaller )
|
|
//
|
|
qboolean SensoryPerception::WithinVisionDistance( const Entity *ent )
|
|
{
|
|
float distance;
|
|
|
|
if ( !ent )
|
|
return false;
|
|
|
|
if ( !act )
|
|
gi.Error( ERR_DROP, "SensoryPerception::WithinVisionDistance -- actor is NULL" );
|
|
|
|
|
|
// Use whichever is less : the actor's vision distance or the distance of the farplane (fog)
|
|
if ( ( world->farplane_distance != 0.0f ) && ( world->farplane_distance < _vision_distance ) )
|
|
distance = world->farplane_distance;
|
|
else
|
|
distance = _vision_distance;
|
|
|
|
return act->WithinDistance( ent, distance );
|
|
}
|
|
|
|
qboolean SensoryPerception::WithinVisionDistance( const Vector &pos )
|
|
{
|
|
float distance;
|
|
|
|
if ( !act )
|
|
gi.Error( ERR_DROP, "SensoryPerception::WithinVisionDistance -- actor is NULL" );
|
|
|
|
// Use whichever is less : the actor's vision distance or the distance of the farplane (fog)
|
|
if ( ( world->farplane_distance != 0.0f ) && ( world->farplane_distance < _vision_distance ) )
|
|
distance = world->farplane_distance;
|
|
else
|
|
distance = _vision_distance;
|
|
|
|
return act->WithinDistance( pos, distance );
|
|
}
|
|
|
|
//
|
|
// Name: InFOV()
|
|
// Parameters: Vector &pos -- The position vector to be checked
|
|
// float check_fov -- The fov number to be used
|
|
// float check_fovdot -- the dot product
|
|
// Description: Checks if the passed in pos to see if it is in the FOV
|
|
//
|
|
qboolean SensoryPerception::InFOV( const Vector &pos, float check_fov, float check_fovdot )
|
|
{
|
|
Vector delta;
|
|
float dot;
|
|
Vector temp;
|
|
int tagNum;
|
|
|
|
|
|
if ( !act )
|
|
gi.Error( ERR_DROP, "SensoryPerception::InFOV -- actor is NULL" );
|
|
|
|
if ( check_fov == 360.0f )
|
|
return true;
|
|
temp = act->EyePosition();
|
|
delta = pos - act->EyePosition();
|
|
|
|
if ( !delta.x && !delta.y )
|
|
{
|
|
// special case for straight up and down
|
|
return true;
|
|
}
|
|
|
|
// give better vertical vision
|
|
delta.z = 0;
|
|
|
|
delta.normalize();
|
|
|
|
tagNum = gi.Tag_NumForName( act->edict->s.modelindex, "tag_eyes" );
|
|
|
|
if ( tagNum >= 0 )
|
|
{
|
|
Vector tag_pos;
|
|
Vector forward;
|
|
|
|
act->GetTag( tagNum, &tag_pos, &forward );
|
|
dot = DotProduct( &forward[0] , delta );
|
|
}
|
|
else
|
|
{
|
|
dot = DotProduct( act->orientation[ 0 ], delta );
|
|
}
|
|
|
|
return ( dot > check_fovdot );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Name: InFOV()
|
|
// Parameters: Vector &pos -- The position to be checked
|
|
// Description: Calls another version of InFOV
|
|
//
|
|
qboolean SensoryPerception::InFOV( const Vector &pos )
|
|
{
|
|
return InFOV( pos, _fov, _fovdot );
|
|
}
|
|
|
|
|
|
//
|
|
// Name: InFOV()
|
|
// Parameters: Entity* ent -- Provides the Position to check
|
|
// Description: Calls another version InFOV
|
|
//
|
|
qboolean SensoryPerception::InFOV( const Entity *ent )
|
|
{
|
|
return InFOV( ent->centroid );
|
|
}
|
|
|
|
//
|
|
// Name: CanSeeEntity()
|
|
// Parameters: Entity *start - The entity trying to see
|
|
// Entity *target - The entity that needs to be seen
|
|
// qboolean useFOV - take FOV into consideration or not
|
|
// qboolean useVisionDistance - take visionDistance into consideration
|
|
// Description: Wraps a lot of the different CanSee Functions into one
|
|
//
|
|
qboolean SensoryPerception::CanSeeEntity( Entity *start, const Entity *target, qboolean useFOV, qboolean useVisionDistance )
|
|
{
|
|
|
|
// Check for NULL
|
|
if ( !start || !target )
|
|
return false;
|
|
|
|
// Check if This Actor can even see at all
|
|
if ( !ShouldRespondToStimuli( STIMULI_SIGHT ) )
|
|
return false;
|
|
|
|
// Check for FOV
|
|
if ( useFOV )
|
|
{
|
|
if ( !InFOV( target ) )
|
|
return false;
|
|
}
|
|
|
|
// Check for vision distance
|
|
if ( useVisionDistance )
|
|
{
|
|
if ( !WithinVisionDistance( target ) )
|
|
return false;
|
|
}
|
|
|
|
// Do Trace
|
|
trace_t trace;
|
|
Vector p;
|
|
Vector eyePos;
|
|
|
|
p = target->centroid;
|
|
eyePos = vec_zero;
|
|
|
|
// If the start entity is an actor, then we want to add in the eyeposition
|
|
if ( start->isSubclassOf ( Actor ) )
|
|
{
|
|
Actor *a;
|
|
a = (Actor*)start;
|
|
|
|
if ( !a )
|
|
return false;
|
|
|
|
eyePos = a->EyePosition();
|
|
|
|
}
|
|
|
|
// Check if he's visible
|
|
trace = G_Trace( eyePos, vec_zero, vec_zero, p, target, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" );
|
|
|
|
//if ( act->actortype == IS_TEAMMATE )
|
|
// {
|
|
// G_DebugLine( eyePos , target->centroid, 0.0f, 0.0f, 1.0f, 1.0f );
|
|
// G_DebugLine( eyePos , trace.endpos , 1.0f, 0.0f, 1.0f, 1.0f );
|
|
// }
|
|
|
|
if ( ( trace.fraction == 1.0f ) || ( trace.ent == target->edict ) )
|
|
return true;
|
|
|
|
// Check if his head is visible
|
|
p.z = target->absmax.z;
|
|
trace = G_Trace( eyePos, vec_zero, vec_zero, p, target, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" );
|
|
if ( trace.fraction == 1.0f || trace.ent == target->edict )
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Name: CanSeeEntity()
|
|
// Parameters: Vector &start -- The starting position
|
|
// Entity *target - The entity that needs to be seen
|
|
// qboolean useFOV - take FOV into consideration or not
|
|
// qboolean useVisionDistance - take visionDistance into consideration
|
|
// Description: Wraps a lot of the different CanSee Functions into one
|
|
//
|
|
qboolean SensoryPerception::CanSeeEntity( const Vector &start , const Entity *target, qboolean useFOV , qboolean useVisionDistance )
|
|
{
|
|
Vector realStart;
|
|
|
|
// Check for NULL
|
|
if ( !target )
|
|
return false;
|
|
|
|
// Check if This Actor can even see at all
|
|
if ( !ShouldRespondToStimuli( STIMULI_SIGHT ) )
|
|
return false;
|
|
|
|
|
|
// Check for FOV
|
|
if ( useFOV )
|
|
{
|
|
if ( !InFOV( target ) )
|
|
return false;
|
|
}
|
|
|
|
// Check for vision distance
|
|
if ( useVisionDistance )
|
|
{
|
|
if ( !WithinVisionDistance( target ) )
|
|
return false;
|
|
}
|
|
|
|
// Do Trace
|
|
trace_t trace;
|
|
Vector p;
|
|
Vector eyePos;
|
|
|
|
realStart = start;
|
|
|
|
p = target->centroid;
|
|
|
|
// Add in the eye offset
|
|
|
|
eyePos = act->EyePosition() - act->origin;
|
|
realStart += eyePos;
|
|
|
|
// Check if he's visible
|
|
trace = G_Trace( realStart, vec_zero, vec_zero, p, act, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" );
|
|
if ( trace.fraction == 1.0f || trace.ent == target->edict )
|
|
return true;
|
|
|
|
// Check if his head is visible
|
|
p.z = target->absmax.z;
|
|
trace = G_Trace( realStart, vec_zero, vec_zero, p, act, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" );
|
|
if ( trace.fraction == 1.0f || trace.ent == target->edict )
|
|
return true;
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//
|
|
// Name: CanSeeEntityComplex
|
|
// Parameters: Entity *start - The entity trying to see
|
|
// Entity *target - The entity that needs to be seen
|
|
// qboolean useFOV - take FOV into consideration or not
|
|
// qboolean useVisionDistance - take visionDistance into consideration
|
|
// Description: More detailed can see check
|
|
//
|
|
qboolean SensoryPerception::CanSeeEntityComplex(Entity *start, Entity *target, qboolean useFOV, qboolean useVisionDistance )
|
|
{
|
|
|
|
// Check for NULL
|
|
if ( !start || !target )
|
|
return false;
|
|
|
|
// Check if This Actor can even see at all
|
|
if ( !ShouldRespondToStimuli( STIMULI_SIGHT ) )
|
|
return false;
|
|
|
|
if ( !act )
|
|
gi.Error( ERR_DROP, "SensoryPerception::CanSeeEntityComplex -- actor is NULL" );
|
|
|
|
// Check if target is alive
|
|
if ( !act->IsEntityAlive( target ) )
|
|
return false;
|
|
|
|
return _CanSeeComplex(start->centroid, target, useFOV, useVisionDistance );
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Name: CanSeeEntityComplex
|
|
// Parameters: Entity *start - The entity trying to see
|
|
// Entity *target - The entity that needs to be seen
|
|
// qboolean useFOV - take FOV into consideration or not
|
|
// qboolean useVisionDistance - take visionDistance into consideration
|
|
// Description: More detailed can see check
|
|
//
|
|
qboolean SensoryPerception::CanSeeEntityComplex( Vector &start, Entity *target, qboolean useFOV , qboolean useVisionDistance )
|
|
{
|
|
// Check for NULL
|
|
if ( !target )
|
|
return false;
|
|
|
|
// Check if This Actor can even see at all
|
|
if ( !ShouldRespondToStimuli( STIMULI_SIGHT ) )
|
|
return false;
|
|
|
|
if ( !act )
|
|
gi.Error( ERR_DROP, "SensoryPerception::CanSeeEntityComplex -- actor is NULL" );
|
|
|
|
|
|
// Check if target is alive
|
|
if ( !act->IsEntityAlive( target ) )
|
|
return false;
|
|
|
|
return _CanSeeComplex(start, target, useFOV, useVisionDistance );
|
|
|
|
}
|
|
|
|
qboolean SensoryPerception::CanSeePosition( const Vector &start, const Vector &position, qboolean useFOV , qboolean useVisionDistance )
|
|
{
|
|
|
|
// Check if This Actor can even see at all
|
|
if ( !ShouldRespondToStimuli( STIMULI_SIGHT ) )
|
|
return false;
|
|
|
|
// Check for FOV
|
|
if ( useFOV )
|
|
{
|
|
if ( !InFOV( position ) )
|
|
return false;
|
|
}
|
|
|
|
// Check for vision distance
|
|
if ( useVisionDistance )
|
|
{
|
|
if ( !WithinVisionDistance( position ) )
|
|
return false;
|
|
}
|
|
|
|
// Do Trace
|
|
trace_t trace;
|
|
Vector eyePos;
|
|
|
|
eyePos = vec_zero;
|
|
|
|
// Check if he's visible
|
|
trace = G_Trace( start, vec_zero, vec_zero, position, act, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" );
|
|
if ( trace.fraction == 1.0f )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Name: _CanSeeComplex
|
|
// Parameters: Vector &start - start position
|
|
// Vector &end - end position
|
|
// qboolean useFOV - take FOV into consideration or not
|
|
// qboolean useVisionDistance
|
|
// Description: Creates a plane to do a more complex check of vision
|
|
//
|
|
qboolean SensoryPerception::_CanSeeComplex( Vector &start, Entity *target , qboolean useFOV, qboolean useVisionDistance )
|
|
{
|
|
Vector d;
|
|
Vector p1;
|
|
Vector p2;
|
|
|
|
if ( !target )
|
|
return false;
|
|
|
|
d = target->centroid - start;
|
|
d.z = 0;
|
|
d.normalize();
|
|
|
|
p1.x = -d.y;
|
|
p1.y = d.x;
|
|
p1 *= max( act->size.x, act->size.y ) * 1.44f * 0.5f;
|
|
p2 = p1;
|
|
|
|
p1.z = act->mins.z;
|
|
p2.z = act->maxs.z;
|
|
if ( CanSeeEntity( start + p1, target , useFOV , useVisionDistance ) )
|
|
{
|
|
return true;
|
|
}
|
|
if ( CanSeeEntity( start + p2, target , useFOV , useVisionDistance ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
p1.z = -p1.z;
|
|
p2.z = -p2.z;
|
|
if ( CanSeeEntity( start - p1, target , useFOV , useVisionDistance ) )
|
|
{
|
|
return true;
|
|
}
|
|
if ( CanSeeEntity( start - p2, target , useFOV , useVisionDistance ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Name: SetNoisePosition()
|
|
// Parameters: Vector pos
|
|
// Description: Sets the _noise_position vector
|
|
//
|
|
void SensoryPerception::SetNoisePosition( const Vector &pos )
|
|
{
|
|
_noise_position = pos;
|
|
}
|
|
|
|
//
|
|
// Name: GetNoisePosition()
|
|
// Parameters: None
|
|
// Description: Returns _noise_position
|
|
//
|
|
Vector SensoryPerception::GetNoisePosition()
|
|
{
|
|
return _noise_position;
|
|
}
|
|
|
|
//
|
|
// Name: SetLastSoundType()
|
|
// Parameters: int soundtype
|
|
// Description: sets _last_soundType
|
|
//
|
|
void SensoryPerception::SetLastSoundType( int soundtype )
|
|
{
|
|
_last_soundType = soundtype;
|
|
}
|
|
|
|
//
|
|
// Name: GetLastSoundType()
|
|
// Parameters: None
|
|
// Description: returns _last_soundType
|
|
//
|
|
int SensoryPerception::GetLastSoundType()
|
|
{
|
|
return _last_soundType;
|
|
}
|
|
|
|
//
|
|
// Name: SetNoiseTime()
|
|
// Parameters: float noisetime
|
|
// Description: sets _noise_time
|
|
//
|
|
void SensoryPerception::SetNoiseTime( float noisetime )
|
|
{
|
|
_noise_time = noisetime;
|
|
}
|
|
|
|
//
|
|
// Name: GetNoiseTime()
|
|
// Parameters: None
|
|
// Description: returns _noise_time
|
|
//
|
|
float SensoryPerception::GetNoiseTime()
|
|
{
|
|
return _noise_time;
|
|
}
|
|
|
|
//
|
|
// Name: SetFOV()
|
|
// Parameters: float fov
|
|
// Description: sets _fov
|
|
//
|
|
void SensoryPerception::SetFOV( float fov )
|
|
{
|
|
_fov = fov;
|
|
}
|
|
|
|
//
|
|
// Name: GetFOV()
|
|
// Parameters: None
|
|
// Description: returns _fov
|
|
//
|
|
float SensoryPerception::GetFOV()
|
|
{
|
|
return _fov;
|
|
}
|
|
|
|
//
|
|
// Name: setFOVdot
|
|
// Parameters: float fov_dot
|
|
// Description: _fovdot
|
|
//
|
|
void SensoryPerception::SetFOVdot( float fov_dot )
|
|
{
|
|
_fovdot = fov_dot;
|
|
}
|
|
|
|
//
|
|
// Name: GetFOVdot()
|
|
// Parameters: none
|
|
// Description: returns _fovdot
|
|
//
|
|
float SensoryPerception::GetFOVdot()
|
|
{
|
|
return _fovdot;
|
|
}
|
|
|
|
//
|
|
// Name: SetVisionDistance()
|
|
// Parameters: float vision_distance
|
|
// Description: Sets _vision_distance
|
|
//
|
|
void SensoryPerception::SetVisionDistance( float vision_distance )
|
|
{
|
|
_vision_distance = vision_distance;
|
|
}
|
|
|
|
//
|
|
// Name: GetVisionDistance()
|
|
// Parameters: None
|
|
// Description: returns _vision_distance
|
|
//
|
|
float SensoryPerception::GetVisionDistance()
|
|
{
|
|
return _vision_distance;
|
|
}
|
|
|
|
//
|
|
// Name: DoArchive
|
|
// Parameters: Archiver &arc -- The archiver object
|
|
// Actor *actor -- The actor
|
|
// Description: Sets the act pointer and calls Archive
|
|
//
|
|
void SensoryPerception::DoArchive( Archiver &arc , Actor *actor )
|
|
{
|
|
Archive( arc );
|
|
if ( actor )
|
|
act = actor;
|
|
else
|
|
gi.Error( ERR_FATAL, "SensoryPerception::DoArchive -- actor is NULL" );
|
|
}
|
|
|
|
//
|
|
// Name: Archive()
|
|
// Parameters: Archiver &arc -- The archiver object
|
|
// Description: Archives the class
|
|
//
|
|
void SensoryPerception::Archive ( Archiver &arc )
|
|
{
|
|
arc.ArchiveInteger ( &_stimuli );
|
|
arc.ArchiveInteger ( &_permanent_stimuli );
|
|
arc.ArchiveVector ( &_noise_position );
|
|
arc.ArchiveInteger ( &_last_soundType );
|
|
arc.ArchiveFloat ( &_noise_time );
|
|
arc.ArchiveFloat ( &_fov );
|
|
arc.ArchiveFloat ( &_fovdot );
|
|
arc.ArchiveFloat ( &_vision_distance );
|
|
arc.ArchiveFloat ( &_nextSenseTime );
|
|
|
|
arc.ArchiveInteger( &_lineOfSight.entNum );
|
|
arc.ArchiveFloat( &_lineOfSight.time );
|
|
arc.ArchiveBoolean( &_lineOfSight.inLineOfSight );
|
|
}
|