1
This commit is contained in:
538
game/server/portal/prop_telescopic_arm.cpp
Normal file
538
game/server/portal/prop_telescopic_arm.cpp
Normal file
@ -0,0 +1,538 @@
|
||||
//========= 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 );
|
||||
}
|
Reference in New Issue
Block a user