diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/client/c_rumble.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/client/c_rumble.cpp')
| -rw-r--r-- | mp/src/game/client/c_rumble.cpp | 1652 |
1 files changed, 826 insertions, 826 deletions
diff --git a/mp/src/game/client/c_rumble.cpp b/mp/src/game/client/c_rumble.cpp index a54858b6..a22ef884 100644 --- a/mp/src/game/client/c_rumble.cpp +++ b/mp/src/game/client/c_rumble.cpp @@ -1,826 +1,826 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Rumble effects mixer for XBox
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-#include "cbase.h"
-#include "c_rumble.h"
-#include "rumble_shared.h"
-#include "inputsystem/iinputsystem.h"
-
-ConVar cl_rumblescale( "cl_rumblescale", "1.0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Scale sensitivity of rumble effects (0 to 1.0)" );
-ConVar cl_debugrumble( "cl_debugrumble", "0", FCVAR_ARCHIVE, "Turn on rumble debugging spew" );
-
-#define MAX_RUMBLE_CHANNELS 3 // Max concurrent rumble effects
-
-#define NUM_WAVE_SAMPLES 30 // Effects play at 10hz
-
-typedef struct
-{
- float amplitude_left[NUM_WAVE_SAMPLES];
- float amplitude_right[NUM_WAVE_SAMPLES];
- int numSamples;
-} RumbleWaveform_t;
-
-//=========================================================
-// Structure for a rumble effect channel. This is akin to
-// a sound channel that is playing a sound.
-//=========================================================
-typedef struct
-{
- float starttime; // When did this effect start playing? (gpGlobals->curtime)
- int waveformIndex; // Type of effect waveform used (an enum from rumble_shared.h)
- int priority; // How important this effect is (for making replacement decisions)
- bool in_use; // Is this channel in use?? (true if effect is currently playing, false if done or otherwise available)
- unsigned char rumbleFlags; // Flags pertaining to the effect currently playing on this channel.
- float scale; // Some effects are updated while they are running.
-} RumbleChannel_t;
-
-//=========================================================
-// This structure contains parameters necessary to generate
-// a sine or sawtooth waveform.
-//=========================================================
-typedef struct tagWaveGenParams
-{
- float cycles; // AKA frequency
- float amplitudescale;
- bool leftChannel; // If false, generating for the right channel
-
- float maxAmplitude; // Clamping
- float minAmplitude;
-
- void Set( float c_cycles, float c_amplitudescale, bool c_leftChannel, float c_minAmplitude, float c_maxAmplitude )
- {
- cycles = c_cycles;
- amplitudescale = c_amplitudescale;
- leftChannel = c_leftChannel;
- minAmplitude = c_minAmplitude;
- maxAmplitude = c_maxAmplitude;
- }
-
- // CTOR
- tagWaveGenParams( float c_cycles, float c_amplitudescale, bool c_leftChannel, float c_minAmplitude, float c_maxAmplitude )
- {
- Set( c_cycles, c_amplitudescale, c_leftChannel, c_minAmplitude, c_maxAmplitude );
- }
-
-} WaveGenParams_t;
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void TerminateWaveform( RumbleWaveform_t *pWaveform, int samples )
-{
- if( samples <= NUM_WAVE_SAMPLES )
- {
- pWaveform->numSamples = samples;
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void EaseInWaveform( RumbleWaveform_t *pWaveform, int samples, bool left )
-{
- float step = 1.0f / ((float)samples);
- float factor = 0.0f;
-
- for( int i = 0 ; i < samples ; i++ )
- {
- if( left )
- {
- pWaveform->amplitude_left[i] *= factor;
- }
- else
- {
- pWaveform->amplitude_right[i] *= factor;
- }
-
- factor += step;
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void EaseOutWaveform( RumbleWaveform_t *pWaveform, int samples, bool left )
-{
- float step = 1.0f / ((float)samples);
- float factor = 0.0f;
-
- int i = NUM_WAVE_SAMPLES - 1;
-
- for( int j = 0 ; j < samples ; j++ )
- {
- if( left )
- {
- pWaveform->amplitude_left[i] *= factor;
- }
- else
- {
- pWaveform->amplitude_right[i] *= factor;
- }
-
- factor += step;
- i--;
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void GenerateSawtoothEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms )
-{
- float delta = params.maxAmplitude - params.minAmplitude;
- int waveLength = NUM_WAVE_SAMPLES / params.cycles;
- float vstep = (delta / waveLength);
-
- float amplitude = params.minAmplitude;
-
- for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ )
- {
- if( params.leftChannel )
- {
- pWaveform->amplitude_left[i] = amplitude;
- }
- else
- {
- pWaveform->amplitude_right[i] = amplitude;
- }
-
- amplitude += vstep;
-
- if( amplitude > params.maxAmplitude )
- {
- amplitude = params.minAmplitude;
- }
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void GenerateSquareWaveEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms )
-{
- int i = 0;
- int j;
-
- int steps = ((float)NUM_WAVE_SAMPLES) / (params.cycles*2.0f);
-
- while( i < NUM_WAVE_SAMPLES )
- {
- for( j = 0 ; j < steps ; j++ )
- {
- if( params.leftChannel )
- {
- pWaveform->amplitude_left[i++] = params.minAmplitude;
- }
- else
- {
- pWaveform->amplitude_right[i++] = params.minAmplitude;
- }
- }
- for( j = 0 ; j < steps ; j++ )
- {
- if( params.leftChannel )
- {
- pWaveform->amplitude_left[i++] = params.maxAmplitude;
- }
- else
- {
- pWaveform->amplitude_right[i++] = params.maxAmplitude;
- }
- }
- }
-}
-
-//---------------------------------------------------------
-// If you pass a numSamples, this wave will only be that many
-// samples long.
-//---------------------------------------------------------
-void GenerateFlatEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms )
-{
- for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ )
- {
- if( params.leftChannel )
- {
- pWaveform->amplitude_left[i] = params.maxAmplitude;
- }
- else
- {
- pWaveform->amplitude_right[i] = params.maxAmplitude;
- }
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void GenerateSineWaveEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms )
-{
- float step = (360.0f * (params.cycles * 0.5f) ) / ((float)NUM_WAVE_SAMPLES);
- float degrees = 180.0f + step; // 180 to start at 0
-
- for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ )
- {
- float radians = DEG2RAD(degrees);
- float value = fabs( sin(radians) );
-
- value *= params.amplitudescale;
-
- if( value < params.minAmplitude )
- value = params.minAmplitude;
-
- if( value > params.maxAmplitude )
- value = params.maxAmplitude;
-
- if( params.leftChannel )
- {
- pWaveform->amplitude_left[i] = value;
- }
- else
- {
- pWaveform->amplitude_right[i] = value;
- }
-
- degrees += step;
- }
-}
-
-//=========================================================
-//=========================================================
-class CRumbleEffects
-{
-public:
- CRumbleEffects()
- {
- Init();
- }
-
- void Init();
- void SetOutputEnabled( bool bEnable );
- void StartEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags );
- void StopEffect( int effectIndex );
- void StopAllEffects();
- void ComputeAmplitudes( RumbleChannel_t *pChannel, float curtime, float *pLeft, float *pRight );
- void UpdateEffects( float curtime );
- void UpdateScreenShakeRumble( float shake, float balance );
-
- RumbleChannel_t *FindExistingChannel( int index );
- RumbleChannel_t *FindAvailableChannel( int priority );
-
-public:
- RumbleChannel_t m_Channels[ MAX_RUMBLE_CHANNELS ];
-
- RumbleWaveform_t m_Waveforms[ NUM_RUMBLE_EFFECTS ];
-
- float m_flScreenShake;
- bool m_bOutputEnabled;
-};
-
-
-CRumbleEffects g_RumbleEffects;
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CRumbleEffects::Init()
-{
- SetOutputEnabled( true );
-
- int i;
-
- for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ )
- {
- m_Channels[i].in_use = false;
- m_Channels[i].priority = 0;
- }
-
- // Every effect defaults to this many samples. Call TerminateWaveform() to trim these.
- for ( i = 0 ; i < NUM_RUMBLE_EFFECTS ; i++ )
- {
- m_Waveforms[i].numSamples = NUM_WAVE_SAMPLES;
- }
-
- // Jeep Idle
- WaveGenParams_t params( 1, 1.0f, false, 0.0f, 0.15f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_JEEP_ENGINE_LOOP], params );
-
- // Pistol
- params.Set( 1, 1.0f, false, 0.0f, 0.5f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_PISTOL], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_PISTOL], 3 );
-
- // SMG1
- params.Set( 1, 1.0f, true, 0.0f, 0.2f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params );
- params.Set( 1, 1.0f, false, 0.0f, 0.4f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_SMG1], 3 );
-
- // AR2
- params.Set( 1, 1.0f, true, 0.0f, 0.5f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2], params );
- params.Set( 1, 1.0f, false, 0.0f, 0.3f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_AR2], 3 );
-
- // AR2 Alt
- params.Set( 1, 1.0f, true, 0.0, 0.5f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], params );
- EaseInWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 5, true );
- params.Set( 1, 1.0f, false, 0.0, 0.7f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], params );
- EaseInWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 5, false );
- TerminateWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 7 );
-
- // 357
- params.Set( 1, 1.0f, true, 0.0f, 0.75f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_357], params );
- params.Set( 1, 1.0f, false, 0.0f, 0.75f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_357], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_357], 2 );
-
- // Shotgun
- params.Set( 1, 1.0f, true, 0.0f, 0.8f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], params );
- params.Set( 1, 1.0f, false, 0.0f, 0.8f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], 3 );
-
- params.Set( 1, 1.0f, true, 0.0f, 1.0f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], params );
- params.Set( 1, 1.0f, false, 0.0f, 1.0f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], 3 );
-
- // RPG Missile
- params.Set( 1, 1.0f, false, 0.0f, 0.3f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_RPG_MISSILE], params );
- EaseOutWaveform( &m_Waveforms[RUMBLE_RPG_MISSILE], 30, false );
- TerminateWaveform( &m_Waveforms[RUMBLE_RPG_MISSILE], 6 );
-
- // Physcannon open forks
- params.Set( 1, 1.0f, false, 0.0f, 0.25f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_PHYSCANNON_OPEN], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_PHYSCANNON_OPEN], 4 );
-
- // Physcannon holding something
- params.Set( 1, 1.0f, true, 0.0f, 0.2f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_PHYSCANNON_LOW], params );
- params.Set( 6, 1.0f, false, 0.0f, 0.25f );
- GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PHYSCANNON_LOW], params );
-
- // Crowbar
- params.Set( 1, 1.0f, false, 0.0f, 0.35f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_CROWBAR_SWING], params );
- EaseOutWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 30, false );
- TerminateWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 4 );
-
- // Airboat gun
- params.Set( 1, 1.0f, false, 0.0f, 0.4f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_AIRBOAT_GUN], params );
- params.Set( 12, 1.0f, true, 0.0f, 0.5f );
- GenerateSawtoothEffect( &m_Waveforms[RUMBLE_AIRBOAT_GUN], params );
-
- // Generic flat effects.
- params.Set( 1, 1.0f, true, 0.0f, 1.0f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_LEFT], params );
-
- params.Set( 1, 1.0f, false, 0.0f, 1.0f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_RIGHT], params );
-
- params.Set( 1, 1.0f, true, 0.0f, 1.0f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_BOTH], params );
- params.Set( 1, 1.0f, false, 0.0f, 1.0f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_BOTH], params );
-
- // Impact from a long fall
- params.Set( 1, 1.0f, false, 0.0f, 0.5f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_LONG], params );
- params.Set( 1, 1.0f, true, 0.0f, 0.5f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_LONG], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_FALL_LONG], 3 );
-
- // Impact from a short fall
- params.Set( 1, 1.0f, false, 0.0f, 0.3f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_SHORT], params );
- params.Set( 1, 1.0f, true, 0.0f, 0.3f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_SHORT], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_FALL_SHORT], 2 );
-
- // Portalgun left (blue) shot
- params.Set( 1, 1.0f, true, 0.0f, 0.3f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_PORTALGUN_LEFT], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_PORTALGUN_LEFT], 2 );
-
- // Portalgun right (red) shot
- params.Set( 1, 1.0f, false, 0.0f, 0.3f );
- GenerateFlatEffect( &m_Waveforms[RUMBLE_PORTALGUN_RIGHT], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_PORTALGUN_RIGHT], 2 );
-
- // Portal failed to place feedback
- params.Set( 12, 1.0f, true, 0.0f, 1.0f );
- GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params );
- params.Set( 12, 1.0f, false, 0.0f, 1.0f );
- GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params );
- TerminateWaveform( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], 6 );
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-RumbleChannel_t *CRumbleEffects::FindExistingChannel( int index )
-{
- RumbleChannel_t *pChannel;
-
- for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ )
- {
- pChannel = &m_Channels[i];
-
- if( pChannel->in_use && pChannel->waveformIndex == index )
- {
- // This effect is already playing. Provide this channel for the
- // effect to be re-started on.
- return pChannel;
- }
- }
-
- return NULL;
-}
-
-//---------------------------------------------------------
-// priority - the priority of the effect we want to play.
-//---------------------------------------------------------
-RumbleChannel_t *CRumbleEffects::FindAvailableChannel( int priority )
-{
- RumbleChannel_t *pChannel;
- int i;
-
- for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ )
- {
- pChannel = &m_Channels[i];
-
- if( !pChannel->in_use )
- {
- return pChannel;
- }
- }
-
- int lowestPriority = priority;
- RumbleChannel_t *pBestChannel = NULL;
- float oldestChannel = FLT_MAX;
-
- // All channels already in use. Find a channel to slam.
- for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ )
- {
- pChannel = &m_Channels[i];
-
- if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) )
- continue;
-
- if( pChannel->priority < lowestPriority )
- {
- // Always happily slam a lower priority sound.
- pBestChannel = pChannel;
- lowestPriority = pChannel->priority;
- }
- else if( pChannel->priority == lowestPriority )
- {
- // Priority is the same, so replace the oldest.
- if( pBestChannel )
- {
- // If we already have a channel of the same priority to discard, make sure we discard the oldest.
- float age = gpGlobals->curtime - pChannel->starttime;
-
- if( age > oldestChannel )
- {
- pBestChannel = pChannel;
- oldestChannel = age;
- }
- }
- else
- {
- // Take it.
- pBestChannel = pChannel;
- oldestChannel = gpGlobals->curtime - pChannel->starttime;
- }
- }
- }
-
- return pBestChannel; // Can still be NULL if we couldn't find a channel to slam.
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CRumbleEffects::SetOutputEnabled( bool bEnable )
-{
- m_bOutputEnabled = bEnable;
-
- if( !bEnable )
- {
- // Tell the hardware to shut down motors right now, in case this gets called
- // and some other process blocks us before the next rumble system update.
- m_flScreenShake = 0.0f;
-
- inputsystem->StopRumble();
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CRumbleEffects::StartEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags )
-{
- if( effectIndex == RUMBLE_STOP_ALL )
- {
- StopAllEffects();
- return;
- }
-
- if( rumbleFlags & RUMBLE_FLAG_STOP )
- {
- StopEffect( effectIndex );
- return;
- }
-
- int priority = 1;
- RumbleChannel_t *pChannel = NULL;
-
- if( (rumbleFlags & RUMBLE_FLAG_RESTART) )
- {
- // Try to find any active instance of this effect and replace it.
- pChannel = FindExistingChannel( effectIndex );
- }
-
- if( (rumbleFlags & RUMBLE_FLAG_ONLYONE) )
- {
- pChannel = FindExistingChannel( effectIndex );
-
- if( pChannel )
- {
- // Bail out. An instance of this effect is already playing.
- return;
- }
- }
-
- if( (rumbleFlags & RUMBLE_FLAG_UPDATE_SCALE) )
- {
- pChannel = FindExistingChannel( effectIndex );
- if( pChannel )
- {
- pChannel->scale = ((float)rumbleData) / 100.0f;
- }
-
- // It's possible to return without finding a rumble to update.
- // This means you tried to update a rumble you never started.
- return;
- }
-
- if( !pChannel )
- {
- pChannel = FindAvailableChannel( priority );
- }
-
- if( pChannel )
- {
- pChannel->waveformIndex = effectIndex;
- pChannel->priority = 1;
- pChannel->starttime = gpGlobals->curtime;
- pChannel->in_use = true;
- pChannel->rumbleFlags = rumbleFlags;
-
- if( rumbleFlags & RUMBLE_FLAG_INITIAL_SCALE )
- {
- pChannel->scale = ((float)rumbleData) / 100.0f;
- }
- else
- {
- pChannel->scale = 1.0f;
- }
- }
-
- if( (rumbleFlags & RUMBLE_FLAG_RANDOM_AMPLITUDE) )
- {
- pChannel->scale = random->RandomFloat( 0.1f, 1.0f );
- }
-}
-
-//---------------------------------------------------------
-// Find all playing effects of this type and stop them.
-//---------------------------------------------------------
-void CRumbleEffects::StopEffect( int effectIndex )
-{
- for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ )
- {
- if( m_Channels[i].in_use && m_Channels[i].waveformIndex == effectIndex )
- {
- m_Channels[i].in_use = false;
- }
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CRumbleEffects::StopAllEffects()
-{
- for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ )
- {
- m_Channels[i].in_use = false;
- }
-
- m_flScreenShake = 0.0f;
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CRumbleEffects::ComputeAmplitudes( RumbleChannel_t *pChannel, float curtime, float *pLeft, float *pRight )
-{
- // How long has this waveform been playing?
- float elapsed = curtime - pChannel->starttime;
-
- if( elapsed >= (NUM_WAVE_SAMPLES/10) )
- {
- if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) )
- {
- // This effect loops. Just fixup the start time and recompute elapsed.
- pChannel->starttime = curtime;
- elapsed = curtime - pChannel->starttime;
- }
- else
- {
- // This effect is done! Should it loop?
- *pLeft = 0;
- *pRight = 0;
- pChannel->in_use = false;
- return;
- }
- }
-
- // Figure out which sample we're playing FROM.
- int seconds = ((int) elapsed);
- int sample = (int)(elapsed*10.0f);
-
- // Get the fraction bit.
- float fraction = elapsed - seconds;
-
- float left, right;
-
- if( sample == m_Waveforms[pChannel->waveformIndex].numSamples )
- {
- // This effect is done. Send zeroes to the mixer for this
- // final frame and then turn the channel off. (Unless it loops!)
-
- if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) )
- {
- // Loop this effect
- pChannel->starttime = gpGlobals->curtime;
-
- // Send the first sample.
- left = m_Waveforms[pChannel->waveformIndex].amplitude_left[0];
- right = m_Waveforms[pChannel->waveformIndex].amplitude_right[0];
- }
- else
- {
- left = 0.0f;
- right = 0.0f;
- pChannel->in_use = false;
- }
- }
- else
- {
- // Use values for the last sample that we have passed
- left = m_Waveforms[pChannel->waveformIndex].amplitude_left[sample];
- right = m_Waveforms[pChannel->waveformIndex].amplitude_right[sample];
- }
-
- left *= pChannel->scale;
- right *= pChannel->scale;
-
- if( cl_debugrumble.GetBool() )
- {
- Msg("Seconds:%d Fraction:%f Sample:%d L:%f R:%f\n", seconds, fraction, sample, left, right );
- }
-
- if( !m_bOutputEnabled )
- {
- // Send zeroes to stop any current rumbling, and to keep it silenced.
- left = 0;
- right = 0;
- }
-
- *pLeft = left;
- *pRight = right;
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CRumbleEffects::UpdateScreenShakeRumble( float shake, float balance )
-{
- if( m_bOutputEnabled )
- {
- m_flScreenShake = shake;
- }
- else
- {
- // Silence
- m_flScreenShake = 0.0f;
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CRumbleEffects::UpdateEffects( float curtime )
-{
- float fLeftMotor = 0.0f;
- float fRightMotor = 0.0f;
-
- for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ )
- {
- // Expire old channels
- RumbleChannel_t *pChannel = & m_Channels[i];
-
- if( pChannel->in_use )
- {
- float left, right;
-
- ComputeAmplitudes( pChannel, curtime, &left, &right );
-
- fLeftMotor += left;
- fRightMotor += right;
- }
- }
-
- // Add in any screenshake
- float shakeLeft = 0.0f;
- float shakeRight = 0.0f;
- if( m_flScreenShake != 0.0f )
- {
- if( m_flScreenShake < 0.0f )
- {
- shakeLeft = fabs( m_flScreenShake );
- }
- else
- {
- shakeRight = m_flScreenShake;
- }
- }
-
- fLeftMotor += shakeLeft;
- fRightMotor += shakeRight;
-
- fLeftMotor *= cl_rumblescale.GetFloat();
- fRightMotor *= cl_rumblescale.GetFloat();
-
- if( engine->IsPaused() )
- {
- // Send nothing when paused.
- fLeftMotor = 0.0f;
- fRightMotor = 0.0f;
- }
-
- inputsystem->SetRumble( fLeftMotor, fRightMotor );
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void StopAllRumbleEffects( void )
-{
- g_RumbleEffects.StopAllEffects();
-
- inputsystem->StopRumble();
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void RumbleEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags )
-{
- g_RumbleEffects.StartEffect( effectIndex, rumbleData, rumbleFlags );
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void UpdateRumbleEffects()
-{
- C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
- if( !localPlayer || !localPlayer->IsAlive() )
- {
- StopAllRumbleEffects();
- return;
- }
-
- g_RumbleEffects.UpdateEffects( gpGlobals->curtime );
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void UpdateScreenShakeRumble( float shake, float balance )
-{
- C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
- if( !localPlayer || !localPlayer->IsAlive() )
- {
- return;
- }
-
- g_RumbleEffects.UpdateScreenShakeRumble( shake, balance );
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void EnableRumbleOutput( bool bEnable )
-{
- g_RumbleEffects.SetOutputEnabled( bEnable );
-}
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Rumble effects mixer for XBox +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "c_rumble.h" +#include "rumble_shared.h" +#include "inputsystem/iinputsystem.h" + +ConVar cl_rumblescale( "cl_rumblescale", "1.0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Scale sensitivity of rumble effects (0 to 1.0)" ); +ConVar cl_debugrumble( "cl_debugrumble", "0", FCVAR_ARCHIVE, "Turn on rumble debugging spew" ); + +#define MAX_RUMBLE_CHANNELS 3 // Max concurrent rumble effects + +#define NUM_WAVE_SAMPLES 30 // Effects play at 10hz + +typedef struct +{ + float amplitude_left[NUM_WAVE_SAMPLES]; + float amplitude_right[NUM_WAVE_SAMPLES]; + int numSamples; +} RumbleWaveform_t; + +//========================================================= +// Structure for a rumble effect channel. This is akin to +// a sound channel that is playing a sound. +//========================================================= +typedef struct +{ + float starttime; // When did this effect start playing? (gpGlobals->curtime) + int waveformIndex; // Type of effect waveform used (an enum from rumble_shared.h) + int priority; // How important this effect is (for making replacement decisions) + bool in_use; // Is this channel in use?? (true if effect is currently playing, false if done or otherwise available) + unsigned char rumbleFlags; // Flags pertaining to the effect currently playing on this channel. + float scale; // Some effects are updated while they are running. +} RumbleChannel_t; + +//========================================================= +// This structure contains parameters necessary to generate +// a sine or sawtooth waveform. +//========================================================= +typedef struct tagWaveGenParams +{ + float cycles; // AKA frequency + float amplitudescale; + bool leftChannel; // If false, generating for the right channel + + float maxAmplitude; // Clamping + float minAmplitude; + + void Set( float c_cycles, float c_amplitudescale, bool c_leftChannel, float c_minAmplitude, float c_maxAmplitude ) + { + cycles = c_cycles; + amplitudescale = c_amplitudescale; + leftChannel = c_leftChannel; + minAmplitude = c_minAmplitude; + maxAmplitude = c_maxAmplitude; + } + + // CTOR + tagWaveGenParams( float c_cycles, float c_amplitudescale, bool c_leftChannel, float c_minAmplitude, float c_maxAmplitude ) + { + Set( c_cycles, c_amplitudescale, c_leftChannel, c_minAmplitude, c_maxAmplitude ); + } + +} WaveGenParams_t; + +//--------------------------------------------------------- +//--------------------------------------------------------- +void TerminateWaveform( RumbleWaveform_t *pWaveform, int samples ) +{ + if( samples <= NUM_WAVE_SAMPLES ) + { + pWaveform->numSamples = samples; + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void EaseInWaveform( RumbleWaveform_t *pWaveform, int samples, bool left ) +{ + float step = 1.0f / ((float)samples); + float factor = 0.0f; + + for( int i = 0 ; i < samples ; i++ ) + { + if( left ) + { + pWaveform->amplitude_left[i] *= factor; + } + else + { + pWaveform->amplitude_right[i] *= factor; + } + + factor += step; + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void EaseOutWaveform( RumbleWaveform_t *pWaveform, int samples, bool left ) +{ + float step = 1.0f / ((float)samples); + float factor = 0.0f; + + int i = NUM_WAVE_SAMPLES - 1; + + for( int j = 0 ; j < samples ; j++ ) + { + if( left ) + { + pWaveform->amplitude_left[i] *= factor; + } + else + { + pWaveform->amplitude_right[i] *= factor; + } + + factor += step; + i--; + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void GenerateSawtoothEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) +{ + float delta = params.maxAmplitude - params.minAmplitude; + int waveLength = NUM_WAVE_SAMPLES / params.cycles; + float vstep = (delta / waveLength); + + float amplitude = params.minAmplitude; + + for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ ) + { + if( params.leftChannel ) + { + pWaveform->amplitude_left[i] = amplitude; + } + else + { + pWaveform->amplitude_right[i] = amplitude; + } + + amplitude += vstep; + + if( amplitude > params.maxAmplitude ) + { + amplitude = params.minAmplitude; + } + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void GenerateSquareWaveEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) +{ + int i = 0; + int j; + + int steps = ((float)NUM_WAVE_SAMPLES) / (params.cycles*2.0f); + + while( i < NUM_WAVE_SAMPLES ) + { + for( j = 0 ; j < steps ; j++ ) + { + if( params.leftChannel ) + { + pWaveform->amplitude_left[i++] = params.minAmplitude; + } + else + { + pWaveform->amplitude_right[i++] = params.minAmplitude; + } + } + for( j = 0 ; j < steps ; j++ ) + { + if( params.leftChannel ) + { + pWaveform->amplitude_left[i++] = params.maxAmplitude; + } + else + { + pWaveform->amplitude_right[i++] = params.maxAmplitude; + } + } + } +} + +//--------------------------------------------------------- +// If you pass a numSamples, this wave will only be that many +// samples long. +//--------------------------------------------------------- +void GenerateFlatEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) +{ + for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ ) + { + if( params.leftChannel ) + { + pWaveform->amplitude_left[i] = params.maxAmplitude; + } + else + { + pWaveform->amplitude_right[i] = params.maxAmplitude; + } + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void GenerateSineWaveEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) +{ + float step = (360.0f * (params.cycles * 0.5f) ) / ((float)NUM_WAVE_SAMPLES); + float degrees = 180.0f + step; // 180 to start at 0 + + for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ ) + { + float radians = DEG2RAD(degrees); + float value = fabs( sin(radians) ); + + value *= params.amplitudescale; + + if( value < params.minAmplitude ) + value = params.minAmplitude; + + if( value > params.maxAmplitude ) + value = params.maxAmplitude; + + if( params.leftChannel ) + { + pWaveform->amplitude_left[i] = value; + } + else + { + pWaveform->amplitude_right[i] = value; + } + + degrees += step; + } +} + +//========================================================= +//========================================================= +class CRumbleEffects +{ +public: + CRumbleEffects() + { + Init(); + } + + void Init(); + void SetOutputEnabled( bool bEnable ); + void StartEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ); + void StopEffect( int effectIndex ); + void StopAllEffects(); + void ComputeAmplitudes( RumbleChannel_t *pChannel, float curtime, float *pLeft, float *pRight ); + void UpdateEffects( float curtime ); + void UpdateScreenShakeRumble( float shake, float balance ); + + RumbleChannel_t *FindExistingChannel( int index ); + RumbleChannel_t *FindAvailableChannel( int priority ); + +public: + RumbleChannel_t m_Channels[ MAX_RUMBLE_CHANNELS ]; + + RumbleWaveform_t m_Waveforms[ NUM_RUMBLE_EFFECTS ]; + + float m_flScreenShake; + bool m_bOutputEnabled; +}; + + +CRumbleEffects g_RumbleEffects; + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::Init() +{ + SetOutputEnabled( true ); + + int i; + + for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + m_Channels[i].in_use = false; + m_Channels[i].priority = 0; + } + + // Every effect defaults to this many samples. Call TerminateWaveform() to trim these. + for ( i = 0 ; i < NUM_RUMBLE_EFFECTS ; i++ ) + { + m_Waveforms[i].numSamples = NUM_WAVE_SAMPLES; + } + + // Jeep Idle + WaveGenParams_t params( 1, 1.0f, false, 0.0f, 0.15f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_JEEP_ENGINE_LOOP], params ); + + // Pistol + params.Set( 1, 1.0f, false, 0.0f, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PISTOL], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PISTOL], 3 ); + + // SMG1 + params.Set( 1, 1.0f, true, 0.0f, 0.2f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params ); + params.Set( 1, 1.0f, false, 0.0f, 0.4f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_SMG1], 3 ); + + // AR2 + params.Set( 1, 1.0f, true, 0.0f, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2], params ); + params.Set( 1, 1.0f, false, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_AR2], 3 ); + + // AR2 Alt + params.Set( 1, 1.0f, true, 0.0, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], params ); + EaseInWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 5, true ); + params.Set( 1, 1.0f, false, 0.0, 0.7f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], params ); + EaseInWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 5, false ); + TerminateWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 7 ); + + // 357 + params.Set( 1, 1.0f, true, 0.0f, 0.75f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_357], params ); + params.Set( 1, 1.0f, false, 0.0f, 0.75f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_357], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_357], 2 ); + + // Shotgun + params.Set( 1, 1.0f, true, 0.0f, 0.8f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], params ); + params.Set( 1, 1.0f, false, 0.0f, 0.8f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], 3 ); + + params.Set( 1, 1.0f, true, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], params ); + params.Set( 1, 1.0f, false, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], 3 ); + + // RPG Missile + params.Set( 1, 1.0f, false, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_RPG_MISSILE], params ); + EaseOutWaveform( &m_Waveforms[RUMBLE_RPG_MISSILE], 30, false ); + TerminateWaveform( &m_Waveforms[RUMBLE_RPG_MISSILE], 6 ); + + // Physcannon open forks + params.Set( 1, 1.0f, false, 0.0f, 0.25f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PHYSCANNON_OPEN], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PHYSCANNON_OPEN], 4 ); + + // Physcannon holding something + params.Set( 1, 1.0f, true, 0.0f, 0.2f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PHYSCANNON_LOW], params ); + params.Set( 6, 1.0f, false, 0.0f, 0.25f ); + GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PHYSCANNON_LOW], params ); + + // Crowbar + params.Set( 1, 1.0f, false, 0.0f, 0.35f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_CROWBAR_SWING], params ); + EaseOutWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 30, false ); + TerminateWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 4 ); + + // Airboat gun + params.Set( 1, 1.0f, false, 0.0f, 0.4f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AIRBOAT_GUN], params ); + params.Set( 12, 1.0f, true, 0.0f, 0.5f ); + GenerateSawtoothEffect( &m_Waveforms[RUMBLE_AIRBOAT_GUN], params ); + + // Generic flat effects. + params.Set( 1, 1.0f, true, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_LEFT], params ); + + params.Set( 1, 1.0f, false, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_RIGHT], params ); + + params.Set( 1, 1.0f, true, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_BOTH], params ); + params.Set( 1, 1.0f, false, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_BOTH], params ); + + // Impact from a long fall + params.Set( 1, 1.0f, false, 0.0f, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_LONG], params ); + params.Set( 1, 1.0f, true, 0.0f, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_LONG], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_FALL_LONG], 3 ); + + // Impact from a short fall + params.Set( 1, 1.0f, false, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_SHORT], params ); + params.Set( 1, 1.0f, true, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_SHORT], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_FALL_SHORT], 2 ); + + // Portalgun left (blue) shot + params.Set( 1, 1.0f, true, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PORTALGUN_LEFT], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PORTALGUN_LEFT], 2 ); + + // Portalgun right (red) shot + params.Set( 1, 1.0f, false, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PORTALGUN_RIGHT], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PORTALGUN_RIGHT], 2 ); + + // Portal failed to place feedback + params.Set( 12, 1.0f, true, 0.0f, 1.0f ); + GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params ); + params.Set( 12, 1.0f, false, 0.0f, 1.0f ); + GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], 6 ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +RumbleChannel_t *CRumbleEffects::FindExistingChannel( int index ) +{ + RumbleChannel_t *pChannel; + + for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + pChannel = &m_Channels[i]; + + if( pChannel->in_use && pChannel->waveformIndex == index ) + { + // This effect is already playing. Provide this channel for the + // effect to be re-started on. + return pChannel; + } + } + + return NULL; +} + +//--------------------------------------------------------- +// priority - the priority of the effect we want to play. +//--------------------------------------------------------- +RumbleChannel_t *CRumbleEffects::FindAvailableChannel( int priority ) +{ + RumbleChannel_t *pChannel; + int i; + + for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + pChannel = &m_Channels[i]; + + if( !pChannel->in_use ) + { + return pChannel; + } + } + + int lowestPriority = priority; + RumbleChannel_t *pBestChannel = NULL; + float oldestChannel = FLT_MAX; + + // All channels already in use. Find a channel to slam. + for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + pChannel = &m_Channels[i]; + + if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) ) + continue; + + if( pChannel->priority < lowestPriority ) + { + // Always happily slam a lower priority sound. + pBestChannel = pChannel; + lowestPriority = pChannel->priority; + } + else if( pChannel->priority == lowestPriority ) + { + // Priority is the same, so replace the oldest. + if( pBestChannel ) + { + // If we already have a channel of the same priority to discard, make sure we discard the oldest. + float age = gpGlobals->curtime - pChannel->starttime; + + if( age > oldestChannel ) + { + pBestChannel = pChannel; + oldestChannel = age; + } + } + else + { + // Take it. + pBestChannel = pChannel; + oldestChannel = gpGlobals->curtime - pChannel->starttime; + } + } + } + + return pBestChannel; // Can still be NULL if we couldn't find a channel to slam. +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::SetOutputEnabled( bool bEnable ) +{ + m_bOutputEnabled = bEnable; + + if( !bEnable ) + { + // Tell the hardware to shut down motors right now, in case this gets called + // and some other process blocks us before the next rumble system update. + m_flScreenShake = 0.0f; + + inputsystem->StopRumble(); + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::StartEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ) +{ + if( effectIndex == RUMBLE_STOP_ALL ) + { + StopAllEffects(); + return; + } + + if( rumbleFlags & RUMBLE_FLAG_STOP ) + { + StopEffect( effectIndex ); + return; + } + + int priority = 1; + RumbleChannel_t *pChannel = NULL; + + if( (rumbleFlags & RUMBLE_FLAG_RESTART) ) + { + // Try to find any active instance of this effect and replace it. + pChannel = FindExistingChannel( effectIndex ); + } + + if( (rumbleFlags & RUMBLE_FLAG_ONLYONE) ) + { + pChannel = FindExistingChannel( effectIndex ); + + if( pChannel ) + { + // Bail out. An instance of this effect is already playing. + return; + } + } + + if( (rumbleFlags & RUMBLE_FLAG_UPDATE_SCALE) ) + { + pChannel = FindExistingChannel( effectIndex ); + if( pChannel ) + { + pChannel->scale = ((float)rumbleData) / 100.0f; + } + + // It's possible to return without finding a rumble to update. + // This means you tried to update a rumble you never started. + return; + } + + if( !pChannel ) + { + pChannel = FindAvailableChannel( priority ); + } + + if( pChannel ) + { + pChannel->waveformIndex = effectIndex; + pChannel->priority = 1; + pChannel->starttime = gpGlobals->curtime; + pChannel->in_use = true; + pChannel->rumbleFlags = rumbleFlags; + + if( rumbleFlags & RUMBLE_FLAG_INITIAL_SCALE ) + { + pChannel->scale = ((float)rumbleData) / 100.0f; + } + else + { + pChannel->scale = 1.0f; + } + } + + if( (rumbleFlags & RUMBLE_FLAG_RANDOM_AMPLITUDE) ) + { + pChannel->scale = random->RandomFloat( 0.1f, 1.0f ); + } +} + +//--------------------------------------------------------- +// Find all playing effects of this type and stop them. +//--------------------------------------------------------- +void CRumbleEffects::StopEffect( int effectIndex ) +{ + for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + if( m_Channels[i].in_use && m_Channels[i].waveformIndex == effectIndex ) + { + m_Channels[i].in_use = false; + } + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::StopAllEffects() +{ + for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + m_Channels[i].in_use = false; + } + + m_flScreenShake = 0.0f; +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::ComputeAmplitudes( RumbleChannel_t *pChannel, float curtime, float *pLeft, float *pRight ) +{ + // How long has this waveform been playing? + float elapsed = curtime - pChannel->starttime; + + if( elapsed >= (NUM_WAVE_SAMPLES/10) ) + { + if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) ) + { + // This effect loops. Just fixup the start time and recompute elapsed. + pChannel->starttime = curtime; + elapsed = curtime - pChannel->starttime; + } + else + { + // This effect is done! Should it loop? + *pLeft = 0; + *pRight = 0; + pChannel->in_use = false; + return; + } + } + + // Figure out which sample we're playing FROM. + int seconds = ((int) elapsed); + int sample = (int)(elapsed*10.0f); + + // Get the fraction bit. + float fraction = elapsed - seconds; + + float left, right; + + if( sample == m_Waveforms[pChannel->waveformIndex].numSamples ) + { + // This effect is done. Send zeroes to the mixer for this + // final frame and then turn the channel off. (Unless it loops!) + + if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) ) + { + // Loop this effect + pChannel->starttime = gpGlobals->curtime; + + // Send the first sample. + left = m_Waveforms[pChannel->waveformIndex].amplitude_left[0]; + right = m_Waveforms[pChannel->waveformIndex].amplitude_right[0]; + } + else + { + left = 0.0f; + right = 0.0f; + pChannel->in_use = false; + } + } + else + { + // Use values for the last sample that we have passed + left = m_Waveforms[pChannel->waveformIndex].amplitude_left[sample]; + right = m_Waveforms[pChannel->waveformIndex].amplitude_right[sample]; + } + + left *= pChannel->scale; + right *= pChannel->scale; + + if( cl_debugrumble.GetBool() ) + { + Msg("Seconds:%d Fraction:%f Sample:%d L:%f R:%f\n", seconds, fraction, sample, left, right ); + } + + if( !m_bOutputEnabled ) + { + // Send zeroes to stop any current rumbling, and to keep it silenced. + left = 0; + right = 0; + } + + *pLeft = left; + *pRight = right; +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::UpdateScreenShakeRumble( float shake, float balance ) +{ + if( m_bOutputEnabled ) + { + m_flScreenShake = shake; + } + else + { + // Silence + m_flScreenShake = 0.0f; + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::UpdateEffects( float curtime ) +{ + float fLeftMotor = 0.0f; + float fRightMotor = 0.0f; + + for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + // Expire old channels + RumbleChannel_t *pChannel = & m_Channels[i]; + + if( pChannel->in_use ) + { + float left, right; + + ComputeAmplitudes( pChannel, curtime, &left, &right ); + + fLeftMotor += left; + fRightMotor += right; + } + } + + // Add in any screenshake + float shakeLeft = 0.0f; + float shakeRight = 0.0f; + if( m_flScreenShake != 0.0f ) + { + if( m_flScreenShake < 0.0f ) + { + shakeLeft = fabs( m_flScreenShake ); + } + else + { + shakeRight = m_flScreenShake; + } + } + + fLeftMotor += shakeLeft; + fRightMotor += shakeRight; + + fLeftMotor *= cl_rumblescale.GetFloat(); + fRightMotor *= cl_rumblescale.GetFloat(); + + if( engine->IsPaused() ) + { + // Send nothing when paused. + fLeftMotor = 0.0f; + fRightMotor = 0.0f; + } + + inputsystem->SetRumble( fLeftMotor, fRightMotor ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void StopAllRumbleEffects( void ) +{ + g_RumbleEffects.StopAllEffects(); + + inputsystem->StopRumble(); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void RumbleEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ) +{ + g_RumbleEffects.StartEffect( effectIndex, rumbleData, rumbleFlags ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void UpdateRumbleEffects() +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( !localPlayer || !localPlayer->IsAlive() ) + { + StopAllRumbleEffects(); + return; + } + + g_RumbleEffects.UpdateEffects( gpGlobals->curtime ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void UpdateScreenShakeRumble( float shake, float balance ) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( !localPlayer || !localPlayer->IsAlive() ) + { + return; + } + + g_RumbleEffects.UpdateScreenShakeRumble( shake, balance ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void EnableRumbleOutput( bool bEnable ) +{ + g_RumbleEffects.SetOutputEnabled( bEnable ); +} |