Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

350 lines
10 KiB

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements a screen shake effect that can also shake physics objects.
//
// NOTE: UTIL_ScreenShake() will only shake players who are on the ground
//
// $NoKeywords: $
//=============================================================================//
// Alien Swarm version, works on player's marine position instead of player position
#include "cbase.h"
#include "shake.h"
#include "physics_saverestore.h"
#include "rope.h"
#include "asw_util_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class CASWPhysicsShake : public IMotionEvent
{
DECLARE_SIMPLE_DATADESC();
public:
virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
Vector contact;
if ( !pObject->GetContactPoint( &contact, NULL ) )
return SIM_NOTHING;
// fudge the force a bit to make it more dramatic
pObject->CalculateForceOffset( m_force * (1.0f + pObject->GetMass()*0.4f), contact, &linear, &angular );
return SIM_LOCAL_FORCE;
}
Vector m_force;
};
BEGIN_SIMPLE_DATADESC( CASWPhysicsShake )
DEFINE_FIELD( m_force, FIELD_VECTOR ),
END_DATADESC()
class CASWEnvShake : public CPointEntity
{
private:
float m_Amplitude;
float m_Frequency;
float m_Duration;
float m_Radius; // radius of 0 means all players
float m_stopTime;
float m_nextShake;
float m_currentAmp;
Vector m_maxForce;
IPhysicsMotionController *m_pShakeController;
CASWPhysicsShake m_shakeCallback;
DECLARE_DATADESC();
public:
DECLARE_CLASS( CASWEnvShake, CPointEntity );
~CASWEnvShake( void );
virtual void Spawn( void );
virtual void OnRestore( void );
inline float Amplitude( void ) { return m_Amplitude; }
inline float Frequency( void ) { return m_Frequency; }
inline float Duration( void ) { return m_Duration; }
inline float Radius( void ) { return m_Radius; }
inline void SetAmplitude( float amplitude ) { m_Amplitude = amplitude; }
inline void SetFrequency( float frequency ) { m_Frequency = frequency; }
inline void SetDuration( float duration ) { m_Duration = duration; }
inline void SetRadius( float radius ) { m_Radius = radius; }
// Input handlers
void InputStartShake( inputdata_t &inputdata );
void InputStopShake( inputdata_t &inputdata );
void InputAmplitude( inputdata_t &inputdata );
void InputFrequency( inputdata_t &inputdata );
// Causes the camera/physics shakes to happen:
void ApplyShake( ShakeCommand_t command );
void Think( void );
};
LINK_ENTITY_TO_CLASS( asw_env_shake, CASWEnvShake );
BEGIN_DATADESC( CASWEnvShake )
DEFINE_KEYFIELD( m_Amplitude, FIELD_FLOAT, "amplitude" ),
DEFINE_KEYFIELD( m_Frequency, FIELD_FLOAT, "frequency" ),
DEFINE_KEYFIELD( m_Duration, FIELD_FLOAT, "duration" ),
DEFINE_KEYFIELD( m_Radius, FIELD_FLOAT, "radius" ),
DEFINE_FIELD( m_stopTime, FIELD_TIME ),
DEFINE_FIELD( m_nextShake, FIELD_TIME ),
DEFINE_FIELD( m_currentAmp, FIELD_FLOAT ),
DEFINE_FIELD( m_maxForce, FIELD_VECTOR ),
DEFINE_PHYSPTR( m_pShakeController ),
DEFINE_EMBEDDED( m_shakeCallback ),
DEFINE_INPUTFUNC( FIELD_VOID, "StartShake", InputStartShake ),
DEFINE_INPUTFUNC( FIELD_VOID, "StopShake", InputStopShake ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Amplitude", InputAmplitude ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Frequency", InputFrequency ),
END_DATADESC()
#define SF_ASW_SHAKE_EVERYONE 0x0001 // Don't check radius
#define SF_ASW_SHAKE_INAIR 0x0004 // Shake players in air
#define SF_ASW_SHAKE_PHYSICS 0x0008 // Shake physically (not just camera)
#define SF_ASW_SHAKE_ROPES 0x0010 // Shake ropes too.
#define SF_ASW_SHAKE_NO_VIEW 0x0020 // DON'T shake the view (only ropes and/or physics objects)
//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CASWEnvShake::~CASWEnvShake( void )
{
if ( m_pShakeController )
{
physenv->DestroyMotionController( m_pShakeController );
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets default member values when spawning.
//-----------------------------------------------------------------------------
void CASWEnvShake::Spawn( void )
{
SetSolid( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
if ( GetSpawnFlags() & SF_ASW_SHAKE_EVERYONE )
{
m_Radius = 0;
}
if ( HasSpawnFlags( SF_ASW_SHAKE_NO_VIEW ) && !HasSpawnFlags( SF_ASW_SHAKE_PHYSICS ) && !HasSpawnFlags( SF_ASW_SHAKE_ROPES ) )
{
DevWarning( "env_shake %s with \"Don't shake view\" spawnflag set without \"Shake physics\" or \"Shake ropes\" spawnflags set.", GetDebugName() );
}
}
//-----------------------------------------------------------------------------
// Purpose: Restore the motion controller
//-----------------------------------------------------------------------------
void CASWEnvShake::OnRestore( void )
{
BaseClass::OnRestore();
if ( m_pShakeController )
{
m_pShakeController->SetEventHandler( &m_shakeCallback );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CASWEnvShake::ApplyShake( ShakeCommand_t command )
{
if ( !HasSpawnFlags( SF_ASW_SHAKE_NO_VIEW ) )
{
bool air = (GetSpawnFlags() & SF_ASW_SHAKE_INAIR) ? true : false;
UTIL_ASW_ScreenShake( GetAbsOrigin(), Amplitude(), Frequency(), Duration(), Radius(), command, air );
}
if ( GetSpawnFlags() & SF_ASW_SHAKE_ROPES )
{
CRopeKeyframe::ShakeRopes( GetAbsOrigin(), Radius(), Frequency() );
}
if ( GetSpawnFlags() & SF_ASW_SHAKE_PHYSICS )
{
if ( !m_pShakeController )
{
m_pShakeController = physenv->CreateMotionController( &m_shakeCallback );
}
// do physics shake
switch( command )
{
case SHAKE_START:
{
m_stopTime = gpGlobals->curtime + Duration();
m_nextShake = 0;
m_pShakeController->ClearObjects();
SetNextThink( gpGlobals->curtime );
m_currentAmp = Amplitude();
CBaseEntity *list[1024];
float radius = Radius();
// probably checked "Shake Everywhere" do a big radius
if ( !radius )
{
radius = MAX_COORD_INTEGER;
}
Vector extents = Vector(radius, radius, radius);
Vector mins = GetAbsOrigin() - extents;
Vector maxs = GetAbsOrigin() + extents;
int count = UTIL_EntitiesInBox( list, 1024, mins, maxs, 0 );
for ( int i = 0; i < count; i++ )
{
//
// Only shake physics entities that players can see. This is one frame out of date
// so it's possible that we could miss objects if a player changed PVS this frame.
//
if ( ( list[i]->GetMoveType() == MOVETYPE_VPHYSICS ) )
{
IPhysicsObject *pPhys = list[i]->VPhysicsGetObject();
if ( pPhys && pPhys->IsMoveable() )
{
m_pShakeController->AttachObject( pPhys, false );
pPhys->Wake();
}
}
}
}
break;
case SHAKE_STOP:
m_pShakeController->ClearObjects();
break;
case SHAKE_AMPLITUDE:
m_currentAmp = Amplitude();
case SHAKE_FREQUENCY:
m_pShakeController->WakeObjects();
break;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Input handler that starts the screen shake.
//-----------------------------------------------------------------------------
void CASWEnvShake::InputStartShake( inputdata_t &inputdata )
{
ApplyShake( SHAKE_START );
}
//-----------------------------------------------------------------------------
// Purpose: Input handler that stops the screen shake.
//-----------------------------------------------------------------------------
void CASWEnvShake::InputStopShake( inputdata_t &inputdata )
{
ApplyShake( SHAKE_STOP );
}
//-----------------------------------------------------------------------------
// Purpose: Handles changes to the shake amplitude from an external source.
//-----------------------------------------------------------------------------
void CASWEnvShake::InputAmplitude( inputdata_t &inputdata )
{
SetAmplitude( inputdata.value.Float() );
ApplyShake( SHAKE_AMPLITUDE );
}
//-----------------------------------------------------------------------------
// Purpose: Handles changes to the shake frequency from an external source.
//-----------------------------------------------------------------------------
void CASWEnvShake::InputFrequency( inputdata_t &inputdata )
{
SetFrequency( inputdata.value.Float() );
ApplyShake( SHAKE_FREQUENCY );
}
//-----------------------------------------------------------------------------
// Purpose: Calculates the physics shake values
//-----------------------------------------------------------------------------
void CASWEnvShake::Think( void )
{
int i;
if ( gpGlobals->curtime > m_nextShake )
{
// Higher frequency means we recalc the extents more often and perturb the display again
m_nextShake = gpGlobals->curtime + (1.0f / Frequency());
// Compute random shake extents (the shake will settle down from this)
for (i = 0; i < 2; i++ )
{
m_maxForce[i] = random->RandomFloat( -1, 1 );
}
// make the force it point mostly up
m_maxForce.z = 4;
VectorNormalize( m_maxForce );
m_maxForce *= m_currentAmp * 400; // amplitude is the acceleration of a 100kg object
}
float fraction = ( m_stopTime - gpGlobals->curtime ) / Duration();
if ( fraction < 0 )
{
m_pShakeController->ClearObjects();
return;
}
float freq = 0;
// Ramp up frequency over duration
if ( fraction )
{
freq = (Frequency() / fraction);
}
// square fraction to approach zero more quickly
fraction *= fraction;
// Sine wave that slowly settles to zero
fraction = fraction * sin( gpGlobals->curtime * freq );
// Add to view origin
for ( i = 0; i < 3; i++ )
{
// store the force in the controller callback
m_shakeCallback.m_force[i] = m_maxForce[i] * fraction;
}
// Drop amplitude a bit, less for higher frequency shakes
m_currentAmp -= m_currentAmp * ( gpGlobals->frametime / (Duration() * Frequency()) );
SetNextThink( gpGlobals->curtime );
}
//------------------------------------------------------------------------------
// Purpose: Console command to cause a screen shake.
//------------------------------------------------------------------------------
void CC_ASW_Shake( void )
{
CBasePlayer *pPlayer = UTIL_GetCommandClient();
if (pPlayer)
{
UTIL_ASW_ScreenShake( pPlayer->WorldSpaceCenter(), 25.0, 150.0, 1.0, 750, SHAKE_START );
}
}
static ConCommand asw_shake("asw_shake", CC_ASW_Shake, "Shake the screen.", FCVAR_CHEAT );