|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the big scary boom-boom machine Antlions fear.
//
//=============================================================================//
#include "cbase.h"
#include "baseentity.h"
#include "rotorwash.h"
#include "soundenvelope.h"
#include "engine/IEngineSound.h"
#include "te_effect_dispatch.h"
#include "point_posecontroller.h"
#include "prop_portal_shared.h"
#define TELESCOPE_ENABLE_TIME 1.0f
#define TELESCOPE_DISABLE_TIME 2.0f
#define TELESCOPE_ROTATEX_TIME 0.5f
#define TELESCOPE_ROTATEY_TIME 0.5f
#define TELESCOPING_ARM_MODEL_NAME "models/props/telescopic_arm.mdl"
#define DEBUG_TELESCOPIC_ARM_AIM 1
class CPropTelescopicArm : public CBaseAnimating { public: DECLARE_CLASS( CPropTelescopicArm, CBaseAnimating ); DECLARE_DATADESC();
virtual void UpdateOnRemove( void ); virtual void Spawn( void ); virtual void Precache( void ); virtual void Activate ( void );
void DisabledThink( void ); void EnabledThink( void );
void AimAt( Vector vTarget ); bool TestLOS( const Vector& vAimPoint ); void SetTarget( const char *pTargetName ); void SetTarget( CBaseEntity *pTarget );
void InputDisable( inputdata_t &inputdata ); void InputEnable( inputdata_t &inputdata );
void InputSetTarget( inputdata_t &inputdata ); void InputTargetPlayer( inputdata_t &inputdata );
private:
Vector FindTargetAimPoint( void ); Vector FindAimPointThroughPortal ( const CProp_Portal* pPortal ); bool m_bEnabled; bool m_bCanSeeTarget; int m_iFrontMarkerAttachment;
EHANDLE m_hRotXPoseController; EHANDLE m_hRotYPoseController; EHANDLE m_hTelescopicPoseController;
EHANDLE m_hAimTarget; COutputEvent m_OnLostTarget; COutputEvent m_OnFoundTarget; };
LINK_ENTITY_TO_CLASS( prop_telescopic_arm, CPropTelescopicArm );
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CPropTelescopicArm ) DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), DEFINE_FIELD( m_bCanSeeTarget, FIELD_BOOLEAN ), DEFINE_FIELD( m_iFrontMarkerAttachment, FIELD_INTEGER ), DEFINE_FIELD( m_hRotXPoseController, FIELD_EHANDLE ), DEFINE_FIELD( m_hRotYPoseController, FIELD_EHANDLE ), DEFINE_FIELD( m_hTelescopicPoseController, FIELD_EHANDLE ), DEFINE_FIELD( m_hAimTarget, FIELD_EHANDLE ), DEFINE_THINKFUNC( DisabledThink ), DEFINE_THINKFUNC( EnabledThink ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ), DEFINE_INPUTFUNC( FIELD_VOID, "TargetPlayer", InputTargetPlayer ), DEFINE_OUTPUT ( m_OnLostTarget, "OnLostTarget" ), DEFINE_OUTPUT ( m_OnFoundTarget, "OnFoundTarget" ), END_DATADESC()
void CPropTelescopicArm::UpdateOnRemove( void ) { CPoseController *pPoseController;
pPoseController = static_cast<CPoseController*>( m_hRotXPoseController.Get() ); if ( pPoseController ) UTIL_Remove( pPoseController ); m_hRotXPoseController = 0;
pPoseController = static_cast<CPoseController*>( m_hRotYPoseController.Get() ); if ( pPoseController ) UTIL_Remove( pPoseController ); m_hRotYPoseController = 0;
pPoseController = static_cast<CPoseController*>( m_hTelescopicPoseController.Get() ); if ( pPoseController ) UTIL_Remove( pPoseController ); m_hTelescopicPoseController = 0; }
void CPropTelescopicArm::Spawn( void ) { char *szModel = (char *)STRING( GetModelName() ); if (!szModel || !*szModel) { szModel = TELESCOPING_ARM_MODEL_NAME; SetModelName( AllocPooledString(szModel) ); }
Precache(); SetModel( szModel );
SetSolid( SOLID_VPHYSICS ); SetMoveType( MOVETYPE_PUSH ); VPhysicsInitStatic();
BaseClass::Spawn();
m_bEnabled = false; m_bCanSeeTarget = false;
SetThink( &CPropTelescopicArm::DisabledThink ); SetNextThink( gpGlobals->curtime + 1.0f );
int iSequence = SelectHeaviestSequence ( ACT_IDLE );
if ( iSequence != ACT_INVALID ) { SetSequence( iSequence ); ResetSequenceInfo();
//Do this so we get the nice ramp-up effect.
m_flPlaybackRate = random->RandomFloat( 0.0f, 1.0f ); }
m_iFrontMarkerAttachment = LookupAttachment( "Front_marker" );
CPoseController *pPoseController;
pPoseController = static_cast<CPoseController*>( CreateEntityByName( "point_posecontroller" ) ); DispatchSpawn( pPoseController ); if ( pPoseController ) { pPoseController->SetProp( this ); pPoseController->SetInterpolationWrap( true ); pPoseController->SetPoseParameterName( "rot_x" ); m_hRotXPoseController = pPoseController; }
pPoseController = static_cast<CPoseController*>( CreateEntityByName( "point_posecontroller" ) ); DispatchSpawn( pPoseController ); if ( pPoseController ) { pPoseController->SetProp( this ); pPoseController->SetInterpolationWrap( true ); pPoseController->SetPoseParameterName( "rot_y" ); m_hRotYPoseController = pPoseController; }
pPoseController = static_cast<CPoseController*>( CreateEntityByName( "point_posecontroller" ) ); DispatchSpawn( pPoseController ); if ( pPoseController ) { pPoseController->SetProp( this ); pPoseController->SetPoseParameterName( "telescopic" ); m_hTelescopicPoseController = pPoseController; } }
void CPropTelescopicArm::Precache( void ) { BaseClass::Precache();
PrecacheModel( STRING( GetModelName() ) ); PrecacheScriptSound( "coast.thumper_hit" ); PrecacheScriptSound( "coast.thumper_ambient" ); PrecacheScriptSound( "coast.thumper_dust" ); PrecacheScriptSound( "coast.thumper_startup" ); PrecacheScriptSound( "coast.thumper_shutdown" ); PrecacheScriptSound( "coast.thumper_large_hit" ); }
void CPropTelescopicArm::Activate( void ) { BaseClass::Activate(); }
void CPropTelescopicArm::DisabledThink( void ) { SetNextThink( gpGlobals->curtime + 1.0 ); }
void CPropTelescopicArm::EnabledThink( void ) { CBaseEntity *pTarget = m_hAimTarget.Get();
if ( !pTarget ) { //SetTarget ( UTIL_PlayerByIndex( 1 ) );
// Default to targeting a player
for( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if( pPlayer && FVisible( pPlayer ) && pPlayer->IsAlive() ) { pTarget = pPlayer; break; } } if( pTarget == NULL ) { //search again, but don't require the player to be visible
for( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if( pPlayer && pPlayer->IsAlive() ) { pTarget = pPlayer; break; } }
if( pTarget == NULL ) { //search again, but don't require the player to be visible or alive
for( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if( pPlayer ) { pTarget = pPlayer; break; } } } }
if( pTarget ) SetTarget( pTarget ); }
if ( pTarget ) { // Aim at the center of the abs box
Vector vAimPoint = FindTargetAimPoint(); Assert ( vAimPoint != vec3_invalid ); AimAt( vAimPoint );
// We have direct line of sight to our target
if ( TestLOS ( vAimPoint ) ) { // Just aquired LOS
if ( !m_bCanSeeTarget ) { m_OnFoundTarget.FireOutput( m_hAimTarget, this ); } m_bCanSeeTarget = true; } // No LOS to target
else { // Just lost LOS
if ( m_bCanSeeTarget ) { m_OnLostTarget.FireOutput( m_hAimTarget, this ); } m_bCanSeeTarget = false; } }
SetNextThink( gpGlobals->curtime + 0.1 ); }
//-----------------------------------------------------------------------------
// Purpose: Finds the point to aim at in order to see the target entity.
// Note: Also considers aim paths through portals.
// Output : Point of the target
//-----------------------------------------------------------------------------
Vector CPropTelescopicArm::FindTargetAimPoint( void ) { CBaseEntity *pTarget = m_hAimTarget.Get();
if ( !pTarget ) { // No target to aim at, can't return meaningful info
Warning( "CPropTelescopicArm::FindTargetAimPoint called with no valid target entity." ); return vec3_invalid; } else { Vector vFrontPoint; GetAttachment( m_iFrontMarkerAttachment, vFrontPoint, NULL, NULL, NULL );
// Aim at the target through the world
Vector vAimPoint = pTarget->GetAbsOrigin() + ( pTarget->WorldAlignMins() + pTarget->WorldAlignMaxs() ) * 0.5f; //float fDistToPoint = vFrontPoint.DistToSqr( vAimPoint );
CProp_Portal *pShortestDistPortal = NULL; UTIL_Portal_ShortestDistance( vFrontPoint, vAimPoint, &pShortestDistPortal, true );
Vector ptShortestAimPoint; if( pShortestDistPortal ) { ptShortestAimPoint = FindAimPointThroughPortal( pShortestDistPortal ); if( ptShortestAimPoint == vec3_invalid ) ptShortestAimPoint = vAimPoint; } else { ptShortestAimPoint = vAimPoint; }
return ptShortestAimPoint; } }
//-----------------------------------------------------------------------------
// Purpose: Find the center of the target entity as seen through the specified portal
// Input : pPortal - The portal to look through
// Output : Vector& output point in world space where the target *appears* to be as seen through the portal
//-----------------------------------------------------------------------------
Vector CPropTelescopicArm::FindAimPointThroughPortal( const CProp_Portal* pPortal ) { if ( pPortal && pPortal->m_bActivated ) { CProp_Portal* pLinked = pPortal->m_hLinkedPortal.Get(); CBaseEntity* pTarget = m_hAimTarget.Get();
if ( pLinked && pLinked->m_bActivated && pTarget ) { VMatrix matToPortalView = pLinked->m_matrixThisToLinked; Vector vTargetAimPoint = pTarget->GetAbsOrigin() + ( pTarget->WorldAlignMins() + pTarget->WorldAlignMaxs() ) * 0.5f;
return matToPortalView * vTargetAimPoint; } } // Bad portal pointer, not linked, no target or otherwise failed
return vec3_invalid; }
//-----------------------------------------------------------------------------
// Purpose: Tests if this prop's front point has direct line of sight to it's target entity
// Input : vAimPoint - The point to aim at
// Output : Returns true if target is in direct line of sight, false otherwise.
//-----------------------------------------------------------------------------
bool CPropTelescopicArm::TestLOS( const Vector& vAimPoint ) { // Test for LOS and fire outputs if the sight condition changes
Vector vFaceOrigin; trace_t tr; GetAttachment( m_iFrontMarkerAttachment, vFaceOrigin, NULL, NULL, NULL ); Ray_t ray; ray.Init( vFaceOrigin, vAimPoint ); ray.m_IsRay = true;
// This aim point does hit target, now make sure there are no blocking objects in the way
CTraceFilterWorldAndPropsOnly filter; UTIL_Portal_TraceRay( ray, MASK_SHOT, &filter, &tr ); return !(tr.fraction < 1.0f); }
void CPropTelescopicArm::AimAt( Vector vTarget ) { Vector vFaceOrigin; GetAttachment( m_iFrontMarkerAttachment, vFaceOrigin, NULL, NULL, NULL );
Vector vNormalToTarget = vTarget - vFaceOrigin; VectorNormalize( vNormalToTarget );
VMatrix vWorldToLocalRotation = EntityToWorldTransform(); vNormalToTarget = vWorldToLocalRotation.InverseTR().ApplyRotation( vNormalToTarget );
Vector vUp; GetVectors( NULL, NULL, &vUp );
QAngle qAnglesToTarget; VectorAngles( vNormalToTarget, vUp, qAnglesToTarget );
float fNewX = ( qAnglesToTarget.x + 90.0f ) / 360.0f; float fNewY = qAnglesToTarget.y / 360.0f;
if ( fNewY < 0.0f ) fNewY += 1.0f;
CPoseController *pPoseController = static_cast<CPoseController*>( m_hRotXPoseController.Get() ); if ( pPoseController ) { pPoseController->SetInterpolationTime( TELESCOPE_ROTATEX_TIME ); pPoseController->SetPoseValue( fNewX ); }
pPoseController = static_cast<CPoseController*>( m_hRotYPoseController.Get() ); if ( pPoseController ) { pPoseController->SetInterpolationTime( TELESCOPE_ROTATEY_TIME ); pPoseController->SetPoseValue( fNewY ); } }
void CPropTelescopicArm::SetTarget( const char *pchTargetName ) { CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, pchTargetName, NULL, NULL );
//if ( pTarget == NULL )
// pTarget = UTIL_PlayerByIndex( 1 );
return SetTarget( pTarget ); }
void CPropTelescopicArm::SetTarget( CBaseEntity *pTarget ) { m_hAimTarget = pTarget; }
void CPropTelescopicArm::InputDisable( inputdata_t &inputdata ) { if ( m_bEnabled ) { m_bEnabled = false; EmitSound( "coast.thumper_shutdown" );
CPoseController *pPoseController;
pPoseController = static_cast<CPoseController*>( m_hRotXPoseController.Get() ); if ( pPoseController ) { pPoseController->SetInterpolationTime( TELESCOPE_DISABLE_TIME * 0.5f ); pPoseController->SetPoseValue( 0.0f ); }
pPoseController = static_cast<CPoseController*>( m_hRotYPoseController.Get() ); if ( pPoseController ) { pPoseController->SetInterpolationTime( TELESCOPE_DISABLE_TIME * 0.5f ); pPoseController->SetPoseValue( 0.0f ); }
pPoseController = static_cast<CPoseController*>( m_hTelescopicPoseController.Get() ); if ( pPoseController ) { pPoseController->SetInterpolationTime( TELESCOPE_DISABLE_TIME ); pPoseController->SetPoseValue( 0.0f ); }
SetThink( &CPropTelescopicArm::DisabledThink ); SetNextThink( gpGlobals->curtime + TELESCOPE_DISABLE_TIME ); } }
void CPropTelescopicArm::InputEnable( inputdata_t &inputdata ) { if ( !m_bEnabled ) { m_bEnabled = true;
EmitSound( "coast.thumper_startup" );
CPoseController *pPoseController; pPoseController = static_cast<CPoseController*>( m_hTelescopicPoseController.Get() );
if ( pPoseController ) { pPoseController->SetInterpolationTime( TELESCOPE_ENABLE_TIME ); pPoseController->SetPoseValue( 1.0f ); }
SetThink( &CPropTelescopicArm::EnabledThink ); SetNextThink( gpGlobals->curtime + TELESCOPE_ENABLE_TIME ); } }
void CPropTelescopicArm::InputSetTarget( inputdata_t &inputdata ) { SetTarget( inputdata.value.String() ); }
void CPropTelescopicArm::InputTargetPlayer( inputdata_t &inputdata ) { //SetTarget( UTIL_PlayerByIndex( 1 ) );
CBaseEntity *pTarget = NULL; for( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if( pPlayer && FVisible( pPlayer ) && pPlayer->IsAlive() ) { pTarget = pPlayer; break; } } if( pTarget == NULL ) { //search again, but don't require the player to be visible
for( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if( pPlayer && pPlayer->IsAlive() ) { pTarget = pPlayer; break; } }
if( pTarget == NULL ) { //search again, but don't require the player to be visible or alive
for( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if( pPlayer ) { pTarget = pPlayer; break; } } } }
if( pTarget ) SetTarget( pTarget ); }
|