source-engine/game/server/portal/prop_telescopic_arm.cpp
2022-04-16 12:05:19 +03:00

538 lines
15 KiB
C++

//========= 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 );
}