From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/client/c_rumble.cpp | 826 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 826 insertions(+) create mode 100644 mp/src/game/client/c_rumble.cpp (limited to 'mp/src/game/client/c_rumble.cpp') diff --git a/mp/src/game/client/c_rumble.cpp b/mp/src/game/client/c_rumble.cpp new file mode 100644 index 00000000..a54858b6 --- /dev/null +++ b/mp/src/game/client/c_rumble.cpp @@ -0,0 +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 ); +} -- cgit v1.2.3