diff options
Diffstat (limited to 'game/server/hl2/ai_spotlight.cpp')
| -rw-r--r-- | game/server/hl2/ai_spotlight.cpp | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/game/server/hl2/ai_spotlight.cpp b/game/server/hl2/ai_spotlight.cpp new file mode 100644 index 0000000..621068a --- /dev/null +++ b/game/server/hl2/ai_spotlight.cpp @@ -0,0 +1,411 @@ +//========= 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(); +} + |