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.
335 lines
8.8 KiB
335 lines
8.8 KiB
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
#include "cbase.h"
|
|
#include "c_triggers.h"
|
|
#include "in_buttons.h"
|
|
#include "c_func_brush.h"
|
|
#include "collisionutils.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
IMPLEMENT_CLIENTCLASS_DT( C_BaseTrigger, DT_BaseTrigger, CBaseTrigger )
|
|
RecvPropBool( RECVINFO( m_bClientSidePredicted ) ),
|
|
RecvPropInt( RECVINFO( m_spawnflags ) ),
|
|
END_RECV_TABLE()
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Disables auto movement on players that touch it
|
|
//-----------------------------------------------------------------------------
|
|
class C_TriggerPlayerMovement : public C_BaseTrigger
|
|
{
|
|
public:
|
|
DECLARE_CLASS( C_TriggerPlayerMovement, C_BaseTrigger );
|
|
DECLARE_CLIENTCLASS();
|
|
|
|
C_TriggerPlayerMovement();
|
|
~C_TriggerPlayerMovement();
|
|
|
|
void StartTouch( C_BaseEntity *pOther );
|
|
void EndTouch( C_BaseEntity *pOther );
|
|
|
|
protected:
|
|
|
|
virtual void UpdatePartitionListEntry();
|
|
|
|
public:
|
|
C_TriggerPlayerMovement *m_pNext;
|
|
};
|
|
|
|
IMPLEMENT_CLIENTCLASS_DT( C_TriggerPlayerMovement, DT_TriggerPlayerMovement, CTriggerPlayerMovement )
|
|
END_RECV_TABLE()
|
|
|
|
C_EntityClassList< C_TriggerPlayerMovement > g_TriggerPlayerMovementList;
|
|
template<> C_TriggerPlayerMovement *C_EntityClassList<C_TriggerPlayerMovement>::m_pClassList = NULL;
|
|
|
|
C_TriggerPlayerMovement::C_TriggerPlayerMovement()
|
|
{
|
|
g_TriggerPlayerMovementList.Insert( this );
|
|
}
|
|
|
|
C_TriggerPlayerMovement::~C_TriggerPlayerMovement()
|
|
{
|
|
g_TriggerPlayerMovementList.Remove( this );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Little enumeration class used to try touching all triggers
|
|
//-----------------------------------------------------------------------------
|
|
template< class T >
|
|
class CFastTouchTriggers
|
|
{
|
|
public:
|
|
CFastTouchTriggers( C_BaseEntity *pEnt, T *pTriggers ) : m_pEnt( pEnt ), m_pTriggers( pTriggers )
|
|
{
|
|
m_pCollide = pEnt->GetCollideable();
|
|
m_nRequiredTriggerFlags = m_pCollide->GetRequiredTriggerFlags();
|
|
Assert( m_pCollide );
|
|
|
|
Vector vecMins, vecMaxs;
|
|
CM_GetCollideableTriggerTestBox( m_pCollide, &vecMins, &vecMaxs );
|
|
const Vector &vecStart = m_pCollide->GetCollisionOrigin();
|
|
m_Ray.Init( vecStart, vecStart, vecMins, vecMaxs );
|
|
}
|
|
|
|
FORCEINLINE void CM_GetCollideableTriggerTestBox( ICollideable *pCollide, Vector *pMins, Vector *pMaxs )
|
|
{
|
|
if ( pCollide->GetSolid() == SOLID_BBOX )
|
|
{
|
|
*pMins = pCollide->OBBMins();
|
|
*pMaxs = pCollide->OBBMaxs();
|
|
}
|
|
else
|
|
{
|
|
const Vector &vecStart = pCollide->GetCollisionOrigin();
|
|
pCollide->WorldSpaceSurroundingBounds( pMins, pMaxs );
|
|
*pMins -= vecStart;
|
|
*pMaxs -= vecStart;
|
|
}
|
|
}
|
|
|
|
FORCEINLINE void Check( T *pEntity, bool bIgnoreTriggerSolidFlags )
|
|
{
|
|
// Hmmm.. everything in this list should be a trigger....
|
|
ICollideable *pTriggerCollideable = pEntity->GetCollideable();
|
|
int nTriggerSolidFlags = pTriggerCollideable->GetSolidFlags();
|
|
if ( !bIgnoreTriggerSolidFlags
|
|
&& m_nRequiredTriggerFlags && ( nTriggerSolidFlags & m_nRequiredTriggerFlags ) == m_nRequiredTriggerFlags )
|
|
return;
|
|
|
|
if ( nTriggerSolidFlags & FSOLID_USE_TRIGGER_BOUNDS )
|
|
{
|
|
Vector vecTriggerMins, vecTriggerMaxs;
|
|
pTriggerCollideable->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
|
|
if ( !IsBoxIntersectingRay( vecTriggerMins, vecTriggerMaxs, m_Ray ) )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
trace_t tr;
|
|
enginetrace->ClipRayToCollideable( m_Ray, MASK_SOLID, pTriggerCollideable, &tr );
|
|
if ( !(tr.contents & MASK_SOLID) )
|
|
return;
|
|
}
|
|
|
|
trace_t tr;
|
|
UTIL_ClearTrace( tr );
|
|
tr.endpos = (m_pEnt->GetAbsOrigin() + pEntity->GetAbsOrigin()) * 0.5;
|
|
m_pEnt->PhysicsMarkEntitiesAsTouching( pEntity, tr );
|
|
}
|
|
|
|
FORCEINLINE void Run( bool bIgnoreTriggerSolidFlags = false )
|
|
{
|
|
for ( T *trigger = m_pTriggers; trigger ; trigger = trigger->m_pNext )
|
|
{
|
|
if ( trigger->IsDormant() )
|
|
continue;
|
|
Check( trigger, bIgnoreTriggerSolidFlags );
|
|
}
|
|
}
|
|
|
|
Ray_t m_Ray;
|
|
|
|
private:
|
|
C_BaseEntity *m_pEnt;
|
|
ICollideable *m_pCollide;
|
|
uint m_nRequiredTriggerFlags;
|
|
T *m_pTriggers;
|
|
};
|
|
|
|
void TouchTriggerPlayerMovement( C_BaseEntity *pEntity )
|
|
{
|
|
CFastTouchTriggers< C_TriggerPlayerMovement > helper( pEntity, g_TriggerPlayerMovementList.m_pClassList );
|
|
helper.Run();
|
|
}
|
|
|
|
|
|
void C_TriggerPlayerMovement::UpdatePartitionListEntry()
|
|
{
|
|
if ( !m_bClientSidePredicted )
|
|
{
|
|
BaseClass::UpdatePartitionListEntry();
|
|
return;
|
|
}
|
|
|
|
::partition->RemoveAndInsert(
|
|
PARTITION_CLIENT_SOLID_EDICTS | PARTITION_CLIENT_RESPONSIVE_EDICTS | PARTITION_CLIENT_NON_STATIC_EDICTS, // remove
|
|
PARTITION_CLIENT_TRIGGER_ENTITIES, // add
|
|
CollisionProp()->GetPartitionHandle() );
|
|
}
|
|
|
|
void C_TriggerPlayerMovement::StartTouch( C_BaseEntity *pOther )
|
|
{
|
|
C_BasePlayer *pPlayer = ToBasePlayer( pOther );
|
|
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
if ( HasSpawnFlags( SF_TRIGGER_AUTO_DUCK ) )
|
|
{
|
|
pPlayer->ForceButtons( IN_DUCK );
|
|
}
|
|
|
|
if ( HasSpawnFlags( SF_TRIGGER_AUTO_WALK ) )
|
|
{
|
|
pPlayer->ForceButtons( IN_SPEED );
|
|
}
|
|
|
|
// UNDONE: Currently this is the only operation this trigger can do
|
|
if ( HasSpawnFlags( SF_TRIGGER_MOVE_AUTODISABLE ) )
|
|
{
|
|
pPlayer->m_Local.m_bAllowAutoMovement = false;
|
|
}
|
|
}
|
|
|
|
void C_TriggerPlayerMovement::EndTouch( C_BaseEntity *pOther )
|
|
{
|
|
C_BasePlayer *pPlayer = ToBasePlayer( pOther );
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
if ( HasSpawnFlags( SF_TRIGGER_AUTO_DUCK ) )
|
|
{
|
|
pPlayer->UnforceButtons( IN_DUCK );
|
|
}
|
|
|
|
if ( HasSpawnFlags( SF_TRIGGER_AUTO_WALK ) )
|
|
{
|
|
pPlayer->UnforceButtons( IN_SPEED );
|
|
}
|
|
|
|
if ( HasSpawnFlags( SF_TRIGGER_MOVE_AUTODISABLE ) )
|
|
{
|
|
pPlayer->m_Local.m_bAllowAutoMovement = true;
|
|
}
|
|
}
|
|
|
|
|
|
IMPLEMENT_CLIENTCLASS_DT( C_BaseVPhysicsTrigger, DT_BaseVPhysicsTrigger, CBaseVPhysicsTrigger )
|
|
//RecvPropBool ( RECVINFO( m_bDisabled ) )
|
|
END_RECV_TABLE()
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Sound operator trigger
|
|
//
|
|
|
|
class C_TriggerSoundOperator : public C_BaseTrigger
|
|
{
|
|
public:
|
|
DECLARE_CLASS( C_TriggerSoundOperator, C_BaseTrigger );
|
|
DECLARE_CLIENTCLASS();
|
|
|
|
C_TriggerSoundOperator();
|
|
~C_TriggerSoundOperator();
|
|
|
|
void StartTouch( C_BaseEntity *pOther );
|
|
void EndTouch( C_BaseEntity *pOther );
|
|
|
|
protected:
|
|
virtual void UpdatePartitionListEntry();
|
|
|
|
int m_nSoundOperator;
|
|
void UpdateSosVar( bool bTouchingNow );
|
|
|
|
public:
|
|
C_TriggerSoundOperator *m_pNext;
|
|
};
|
|
|
|
IMPLEMENT_CLIENTCLASS_DT( C_TriggerSoundOperator, DT_TriggerSoundOperator, CTriggerSoundOperator )
|
|
RecvPropInt( RECVINFO( m_nSoundOperator ) ),
|
|
END_RECV_TABLE()
|
|
|
|
C_EntityClassList< C_TriggerSoundOperator > g_TriggerSoundOperators;
|
|
template<> C_TriggerSoundOperator *C_EntityClassList<C_TriggerSoundOperator>::m_pClassList = NULL;
|
|
|
|
C_TriggerSoundOperator::C_TriggerSoundOperator()
|
|
{
|
|
g_TriggerSoundOperators.Insert( this );
|
|
m_nSoundOperator = 0;
|
|
}
|
|
|
|
C_TriggerSoundOperator::~C_TriggerSoundOperator()
|
|
{
|
|
g_TriggerSoundOperators.Remove( this );
|
|
}
|
|
|
|
|
|
static char const * g_szTriggerSoundOperatorVariables[] = {
|
|
"sosVarCustomVolume1",
|
|
"sosVarCustomVolume2"
|
|
};
|
|
static bool g_bTriggerSoundOperatorVariablesSet[ Q_ARRAYSIZE( g_szTriggerSoundOperatorVariables ) ] = {
|
|
};
|
|
|
|
void TouchTriggerSoundOperator( C_BaseEntity *pEntity )
|
|
{
|
|
CFastTouchTriggers< C_TriggerSoundOperator > helper( pEntity, g_TriggerSoundOperators.m_pClassList );
|
|
helper.Run( true );
|
|
}
|
|
|
|
void UntouchAllTriggerSoundOperator( C_BaseEntity *pEntity )
|
|
{
|
|
for ( int j = 0; j < Q_ARRAYSIZE( g_szTriggerSoundOperatorVariables ); ++ j )
|
|
{
|
|
if ( g_bTriggerSoundOperatorVariablesSet[ j ] )
|
|
{
|
|
g_bTriggerSoundOperatorVariablesSet[ j ] = false;
|
|
engine->SOSSetOpvarFloat( g_szTriggerSoundOperatorVariables[ j ], 0.0f );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void C_TriggerSoundOperator::UpdatePartitionListEntry()
|
|
{
|
|
if ( !m_bClientSidePredicted )
|
|
{
|
|
BaseClass::UpdatePartitionListEntry();
|
|
return;
|
|
}
|
|
|
|
::partition->RemoveAndInsert(
|
|
PARTITION_CLIENT_SOLID_EDICTS | PARTITION_CLIENT_RESPONSIVE_EDICTS | PARTITION_CLIENT_NON_STATIC_EDICTS, // remove
|
|
PARTITION_CLIENT_TRIGGER_ENTITIES, // add
|
|
CollisionProp()->GetPartitionHandle() );
|
|
}
|
|
|
|
void C_TriggerSoundOperator::StartTouch( C_BaseEntity *pOther )
|
|
{
|
|
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
|
|
if ( !pLocalPlayer || ( pOther != pLocalPlayer ) )
|
|
return;
|
|
|
|
UpdateSosVar( true );
|
|
}
|
|
|
|
void C_TriggerSoundOperator::EndTouch( C_BaseEntity *pOther )
|
|
{
|
|
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
|
|
if ( !pLocalPlayer || ( pOther != pLocalPlayer ) )
|
|
return;
|
|
|
|
UpdateSosVar( false );
|
|
}
|
|
|
|
void C_TriggerSoundOperator::UpdateSosVar( bool bTouchingNow )
|
|
{
|
|
int nVar = 0;
|
|
if ( ( m_nSoundOperator >= 0 )
|
|
&& ( m_nSoundOperator < Q_ARRAYSIZE( g_szTriggerSoundOperatorVariables ) ) )
|
|
nVar = m_nSoundOperator;
|
|
char const *szVar = g_szTriggerSoundOperatorVariables[ nVar ];
|
|
g_bTriggerSoundOperatorVariablesSet[ nVar ] = bTouchingNow;
|
|
engine->SOSSetOpvarFloat( szVar, bTouchingNow ? 1.0f : 0.0f );
|
|
}
|
|
|
|
|
|
|