You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
411 lines
12 KiB
411 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "ai_spotlight.h" |
|
#include "ai_basenpc.h" |
|
#include "spotlightend.h" |
|
#include "beam_shared.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Parameters for how the scanner relates to citizens. |
|
//----------------------------------------------------------------------------- |
|
#define SPOTLIGHT_WIDTH 96 |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Save/load |
|
//----------------------------------------------------------------------------- |
|
BEGIN_SIMPLE_DATADESC( CAI_Spotlight ) |
|
|
|
// Robin: Don't save, recreated after restore/transition. |
|
//DEFINE_FIELD( m_hSpotlight, FIELD_EHANDLE ), |
|
//DEFINE_FIELD( m_hSpotlightTarget, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_vSpotlightTargetPos, FIELD_POSITION_VECTOR ), |
|
DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ), |
|
DEFINE_FIELD( m_flSpotlightCurLength, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flSpotlightMaxLength, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flConstraintAngle, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_nHaloSprite, FIELD_MODELINDEX ), |
|
DEFINE_FIELD( m_nSpotlightAttachment, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_nFlags, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_vAngularVelocity, FIELD_QUATERNION ), |
|
|
|
END_DATADESC() |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CAI_Spotlight::CAI_Spotlight() |
|
{ |
|
#ifdef _DEBUG |
|
m_vSpotlightTargetPos.Init(); |
|
m_vSpotlightDir.Init(); |
|
#endif |
|
} |
|
|
|
|
|
CAI_Spotlight::~CAI_Spotlight() |
|
{ |
|
SpotlightDestroy(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CAI_Spotlight::Precache(void) |
|
{ |
|
// Sprites |
|
m_nHaloSprite = GetOuter()->PrecacheModel("sprites/light_glow03.vmt"); |
|
|
|
GetOuter()->PrecacheModel( "sprites/glow_test02.vmt" ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CAI_Spotlight::Init( CAI_BaseNPC *pOuter, int nFlags, float flConstraintAngle, float flMaxLength ) |
|
{ |
|
SetOuter( pOuter ); |
|
m_nFlags = nFlags; |
|
m_flConstraintAngle = flConstraintAngle; |
|
m_flSpotlightMaxLength = flMaxLength; |
|
|
|
// Check for user error |
|
if (m_flSpotlightMaxLength <= 0) |
|
{ |
|
DevMsg("ERROR: Invalid spotlight length <= 0, setting to 500\n"); |
|
m_flSpotlightMaxLength = 500; |
|
} |
|
|
|
Precache(); |
|
|
|
m_vSpotlightTargetPos = vec3_origin; |
|
m_hSpotlight = NULL; |
|
m_hSpotlightTarget = NULL; |
|
|
|
AngleVectors( GetAbsAngles(), &m_vSpotlightDir ); |
|
m_vAngularVelocity.Init( 0, 0, 0, 1 ); |
|
m_flSpotlightCurLength = m_flSpotlightMaxLength; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Computes the spotlight endpoint |
|
//------------------------------------------------------------------------------ |
|
void CAI_Spotlight::ComputeEndpoint( const Vector &vecStartPoint, Vector *pEndPoint ) |
|
{ |
|
// Create the endpoint |
|
trace_t tr; |
|
AI_TraceLine( vecStartPoint, vecStartPoint + m_vSpotlightDir * 2 * m_flSpotlightMaxLength, MASK_OPAQUE, GetOuter(), COLLISION_GROUP_NONE, &tr ); |
|
*pEndPoint = tr.endpos; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
void CAI_Spotlight::SpotlightCreate( int nAttachment, const Vector &vecInitialDir ) |
|
{ |
|
// Make sure we don't already have one |
|
if ( m_hSpotlight != NULL ) |
|
return; |
|
|
|
m_vSpotlightDir = vecInitialDir; |
|
VectorNormalize( m_vSpotlightDir ); |
|
m_nSpotlightAttachment = nAttachment; |
|
|
|
CreateSpotlightEntities(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create the beam & spotlight end for this spotlight. |
|
// Will be re-called after restore/transition |
|
//----------------------------------------------------------------------------- |
|
void CAI_Spotlight::CreateSpotlightEntities( void ) |
|
{ |
|
m_vAngularVelocity.Init( 0, 0, 0, 1 ); |
|
|
|
// Create the endpoint |
|
// Get the initial position... |
|
Vector vecStartPoint; |
|
if ( m_nSpotlightAttachment == 0 ) |
|
{ |
|
vecStartPoint = GetOuter()->GetAbsOrigin(); |
|
} |
|
else |
|
{ |
|
GetOuter()->GetAttachment( m_nSpotlightAttachment, vecStartPoint ); |
|
} |
|
|
|
Vector vecEndPoint; |
|
ComputeEndpoint( vecStartPoint, &vecEndPoint ); |
|
|
|
m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" ); |
|
m_hSpotlightTarget->Spawn(); |
|
m_hSpotlightTarget->SetAbsOrigin( vecEndPoint ); |
|
m_hSpotlightTarget->SetOwnerEntity( GetOuter() ); |
|
m_hSpotlightTarget->SetRenderColor( 255, 255, 255 ); |
|
m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength; |
|
if ( FBitSet (m_nFlags, AI_SPOTLIGHT_NO_DLIGHTS) ) |
|
{ |
|
m_hSpotlightTarget->m_flLightScale = 0.0; |
|
} |
|
else |
|
{ |
|
m_hSpotlightTarget->m_flLightScale = SPOTLIGHT_WIDTH; |
|
} |
|
|
|
// Create the beam |
|
m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", SPOTLIGHT_WIDTH ); |
|
// Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore) |
|
m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY ); |
|
m_hSpotlight->SetColor( 255, 255, 255 ); |
|
m_hSpotlight->SetHaloTexture( m_nHaloSprite ); |
|
m_hSpotlight->SetHaloScale( 32 ); |
|
m_hSpotlight->SetEndWidth( m_hSpotlight->GetWidth() ); |
|
m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) ); |
|
m_hSpotlight->SetBrightness( 32 ); |
|
m_hSpotlight->SetNoise( 0 ); |
|
m_hSpotlight->EntsInit( GetOuter(), m_hSpotlightTarget ); |
|
m_hSpotlight->SetStartAttachment( m_nSpotlightAttachment ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
void CAI_Spotlight::SpotlightDestroy(void) |
|
{ |
|
if ( m_hSpotlight ) |
|
{ |
|
UTIL_Remove(m_hSpotlight); |
|
m_hSpotlight = NULL; |
|
|
|
UTIL_Remove(m_hSpotlightTarget); |
|
m_hSpotlightTarget = NULL; |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
void CAI_Spotlight::SetSpotlightTargetPos( const Vector &vSpotlightTargetPos ) |
|
{ |
|
m_vSpotlightTargetPos = vSpotlightTargetPos; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
void CAI_Spotlight::SetSpotlightTargetDirection( const Vector &vSpotlightTargetDir ) |
|
{ |
|
if ( !m_hSpotlight ) |
|
{ |
|
CreateSpotlightEntities(); |
|
} |
|
|
|
VectorMA( m_hSpotlight->GetAbsStartPos(), 1000.0f, vSpotlightTargetDir, m_vSpotlightTargetPos ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Constrain to cone |
|
//------------------------------------------------------------------------------ |
|
bool CAI_Spotlight::ConstrainToCone( Vector *pDirection ) |
|
{ |
|
Vector vecOrigin, vecForward; |
|
if ( m_nSpotlightAttachment == 0 ) |
|
{ |
|
QAngle vecAngles; |
|
vecAngles = GetOuter()->GetAbsAngles(); |
|
AngleVectors( vecAngles, &vecForward ); |
|
} |
|
else |
|
{ |
|
GetOuter()->GetAttachment( m_nSpotlightAttachment, vecOrigin, &vecForward ); |
|
} |
|
|
|
|
|
if ( m_flConstraintAngle == 0.0f ) |
|
{ |
|
*pDirection = vecForward; |
|
return true; |
|
} |
|
|
|
float flDot = DotProduct( vecForward, *pDirection ); |
|
if ( flDot >= cos( DEG2RAD( m_flConstraintAngle ) ) ) |
|
return false; |
|
|
|
Vector vecAxis; |
|
CrossProduct( *pDirection, vecForward, vecAxis ); |
|
VectorNormalize( vecAxis ); |
|
|
|
Quaternion q; |
|
AxisAngleQuaternion( vecAxis, -m_flConstraintAngle, q ); |
|
|
|
Vector vecResult; |
|
matrix3x4_t rot; |
|
QuaternionMatrix( q, rot ); |
|
VectorRotate( vecForward, rot, vecResult ); |
|
VectorNormalize( vecResult ); |
|
|
|
*pDirection = vecResult; |
|
|
|
return true; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
#define QUAT_BLEND_FACTOR 0.4f |
|
|
|
void CAI_Spotlight::UpdateSpotlightDirection( void ) |
|
{ |
|
if ( !m_hSpotlight ) |
|
{ |
|
CreateSpotlightEntities(); |
|
} |
|
|
|
// Compute the current beam direction |
|
Vector vTargetDir; |
|
VectorSubtract( m_vSpotlightTargetPos, m_hSpotlight->GetAbsStartPos(), vTargetDir ); |
|
VectorNormalize(vTargetDir); |
|
ConstrainToCone( &vTargetDir ); |
|
|
|
// Compute the amount to rotate |
|
float flDot = DotProduct( vTargetDir, m_vSpotlightDir ); |
|
flDot = clamp( flDot, -1.0f, 1.0f ); |
|
float flAngle = AngleNormalize( RAD2DEG( acos( flDot ) ) ); |
|
float flClampedAngle = clamp( flAngle, 0.0f, 45.0f ); |
|
float flBeamTurnRate = SimpleSplineRemapVal( flClampedAngle, 0.0f, 45.0f, 10.0f, 45.0f ); |
|
if ( fabs(flAngle) > flBeamTurnRate * gpGlobals->frametime ) |
|
{ |
|
flAngle = flBeamTurnRate * gpGlobals->frametime; |
|
} |
|
|
|
// Compute the rotation axis |
|
Vector vecRotationAxis; |
|
CrossProduct( m_vSpotlightDir, vTargetDir, vecRotationAxis ); |
|
if ( VectorNormalize( vecRotationAxis ) < 1e-3 ) |
|
{ |
|
vecRotationAxis.Init( 0, 0, 1 ); |
|
} |
|
|
|
// Compute the actual rotation amount, using quat slerp blending |
|
Quaternion desiredQuat, resultQuat; |
|
AxisAngleQuaternion( vecRotationAxis, flAngle, desiredQuat ); |
|
QuaternionSlerp( m_vAngularVelocity, desiredQuat, QUAT_BLEND_FACTOR, resultQuat ); |
|
m_vAngularVelocity = resultQuat; |
|
|
|
// If we're really close, and we're not moving very quickly, slam. |
|
float flActualRotation = AngleNormalize( RAD2DEG(2 * acos(m_vAngularVelocity.w)) ); |
|
if (( flActualRotation < 1e-3 ) && (flAngle < 1e-3 )) |
|
{ |
|
m_vSpotlightDir = vTargetDir; |
|
m_vAngularVelocity.Init( 0, 0, 0, 1 ); |
|
return; |
|
} |
|
|
|
// Update the desired direction |
|
matrix3x4_t rot; |
|
Vector vecNewDir; |
|
QuaternionMatrix( m_vAngularVelocity, rot ); |
|
VectorRotate( m_vSpotlightDir, rot, vecNewDir ); |
|
m_vSpotlightDir = vecNewDir; |
|
VectorNormalize(m_vSpotlightDir); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
void CAI_Spotlight::UpdateSpotlightEndpoint( void ) |
|
{ |
|
if ( !m_hSpotlight ) |
|
{ |
|
CreateSpotlightEntities(); |
|
} |
|
|
|
Vector vecStartPoint, vecEndPoint; |
|
vecStartPoint = m_hSpotlight->GetAbsStartPos(); |
|
ComputeEndpoint( vecStartPoint, &vecEndPoint ); |
|
|
|
// If I'm not facing the spotlight turn it off |
|
Vector vecSpotDir; |
|
VectorSubtract( vecEndPoint, vecStartPoint, vecSpotDir ); |
|
float flBeamLength = VectorNormalize(vecSpotDir); |
|
|
|
m_hSpotlightTarget->SetAbsOrigin( vecEndPoint ); |
|
m_hSpotlightTarget->SetAbsVelocity( vec3_origin ); |
|
m_hSpotlightTarget->m_vSpotlightOrg = vecStartPoint; |
|
m_hSpotlightTarget->m_vSpotlightDir = vecSpotDir; |
|
|
|
// Avoid sudden change in where beam fades out when cross disconinuities |
|
m_flSpotlightCurLength = Lerp( 0.20f, m_flSpotlightCurLength, flBeamLength ); |
|
|
|
// Fade out spotlight end if past max length. |
|
if (m_flSpotlightCurLength > 2*m_flSpotlightMaxLength) |
|
{ |
|
m_hSpotlightTarget->SetRenderColorA( 0 ); |
|
m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength); |
|
} |
|
else if (m_flSpotlightCurLength > m_flSpotlightMaxLength) |
|
{ |
|
m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) ); |
|
m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength); |
|
} |
|
else |
|
{ |
|
m_hSpotlightTarget->SetRenderColorA( 1.0 ); |
|
m_hSpotlight->SetFadeLength(m_flSpotlightCurLength); |
|
} |
|
|
|
// Adjust end width to keep beam width constant |
|
float flNewWidth = SPOTLIGHT_WIDTH * ( flBeamLength / m_flSpotlightMaxLength ); |
|
|
|
flNewWidth = MIN( 100, flNewWidth ); |
|
|
|
m_hSpotlight->SetWidth(flNewWidth); |
|
m_hSpotlight->SetEndWidth(flNewWidth); |
|
|
|
// Adjust width of light on the end. |
|
if ( FBitSet (m_nFlags, AI_SPOTLIGHT_NO_DLIGHTS) ) |
|
{ |
|
m_hSpotlightTarget->m_flLightScale = 0.0; |
|
} |
|
else |
|
{ |
|
m_hSpotlightTarget->m_flLightScale = flNewWidth; |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: Update the direction and position of my spotlight (if it's active) |
|
//------------------------------------------------------------------------------ |
|
void CAI_Spotlight::Update(void) |
|
{ |
|
if ( !m_hSpotlight ) |
|
{ |
|
CreateSpotlightEntities(); |
|
} |
|
|
|
// Update the beam direction |
|
UpdateSpotlightDirection(); |
|
UpdateSpotlightEndpoint(); |
|
} |
|
|
|
|