|
|
//======= Copyright (c) 1996-2005, 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"
#include "iinput.h"
#ifdef _PS3
#include "ps3/ps3_core.h"
#endif
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
void StopAllRumbleEffects( int userID );
ConVar cl_rumblescale( "cl_rumblescale", "1.0", FCVAR_ARCHIVE | FCVAR_SS, "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 per player
#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( int userID, unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ); void StopEffect( int userID, int effectIndex ); void StopAllEffects( int userID ); void ComputeAmplitudes( RumbleChannel_t *pChannel, float curtime, float *pLeft, float *pRight ); void UpdateEffects( int userID, float curtime ); void UpdateScreenShakeRumble( int userID, float shake, float balance );
RumbleChannel_t *FindExistingChannel( int userID, int index ); RumbleChannel_t *FindAvailableChannel( int userID, int priority );
public: RumbleChannel_t m_Channels[ MAX_SPLITSCREEN_PLAYERS ][ MAX_RUMBLE_CHANNELS ];
RumbleWaveform_t m_Waveforms[ NUM_RUMBLE_EFFECTS ];
float m_flScreenShake[ MAX_SPLITSCREEN_PLAYERS ]; bool m_bOutputEnabled; };
CRumbleEffects g_RumbleEffects;
//---------------------------------------------------------
//---------------------------------------------------------
void CRumbleEffects::Init() { SetOutputEnabled( true );
int userID,channel; for( userID = 0 ; userID < MAX_SPLITSCREEN_PLAYERS ; userID++ ) { for( channel = 0 ; channel < MAX_RUMBLE_CHANNELS ; channel++ ) { m_Channels[userID][channel].in_use = false; m_Channels[userID][channel].priority = 0; } }
// Every effect defaults to this many samples. Call TerminateWaveform() to trim these.
for ( int 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.6f ); GenerateFlatEffect( &m_Waveforms[RUMBLE_PISTOL], params ); TerminateWaveform( &m_Waveforms[RUMBLE_PISTOL], 1 );
// SMG1
params.Set( 1, 1.0f, true, 0.0f, 0.4f ); GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params ); params.Set( 1, 1.0f, false, 0.0f, 0.3f ); GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params ); TerminateWaveform( &m_Waveforms[RUMBLE_SMG1], 1 );
// AR2
params.Set( 1, 1.0f, true, 0.0f, 0.55f ); 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], 1 );
// 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.7f ); GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], params ); params.Set( 1, 1.0f, false, 0.0f, 0.7f ); 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.25f ); GenerateFlatEffect( &m_Waveforms[RUMBLE_CROWBAR_SWING], params ); EaseOutWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 30, false ); TerminateWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 3 );
// 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( 4, 1.0f, true, 0.0f, 0.25f ); GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params ); params.Set( 4, 1.0f, false, 0.0f, 0.25f ); GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params ); TerminateWaveform( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], 6 );
// RUMBLE_DMG_LOW (copied from 357)
params.Set( 1, 1.0f, true, 0.0f, 0.75f ); GenerateFlatEffect( &m_Waveforms[RUMBLE_DMG_LOW], params ); params.Set( 1, 1.0f, false, 0.0f, 0.75f ); GenerateFlatEffect( &m_Waveforms[RUMBLE_DMG_LOW], params ); TerminateWaveform( &m_Waveforms[RUMBLE_DMG_LOW], 2 );
// RUMBLE_DMG_HIGH (copied from AR2)
params.Set( 1, 1.0f, true, 0.0f, 0.5f ); GenerateFlatEffect( &m_Waveforms[RUMBLE_DMG_HIGH], params ); params.Set( 1, 1.0f, false, 0.0f, 0.3f ); GenerateFlatEffect( &m_Waveforms[RUMBLE_DMG_HIGH], params ); TerminateWaveform( &m_Waveforms[RUMBLE_DMG_HIGH], 1 );
}
//---------------------------------------------------------
//---------------------------------------------------------
RumbleChannel_t *CRumbleEffects::FindExistingChannel( int userID, int index ) { RumbleChannel_t *pChannel;
for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) { pChannel = &m_Channels[userID][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 userID, int priority ) { RumbleChannel_t *pChannel; int i;
for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) { pChannel = &m_Channels[userID][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. Make sure it belongs to this userID
for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) { pChannel = &m_Channels[userID][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.
for( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS ; i++ ) { m_flScreenShake[ i ] = 0.0f; StopAllRumbleEffects( i ); } } }
//---------------------------------------------------------
//---------------------------------------------------------
void CRumbleEffects::StartEffect( int userID, unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ) { if( effectIndex == RUMBLE_STOP_ALL ) { StopAllEffects( userID ); return; }
if( rumbleFlags & RUMBLE_FLAG_STOP ) { StopEffect( userID, effectIndex ); return; }
if ( !input->ControllerModeActive() ) 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( userID, effectIndex ); }
if( (rumbleFlags & RUMBLE_FLAG_ONLYONE) ) { pChannel = FindExistingChannel( userID, effectIndex );
if( pChannel ) { // Bail out. An instance of this effect is already playing.
return; } }
if( (rumbleFlags & RUMBLE_FLAG_UPDATE_SCALE) ) { pChannel = FindExistingChannel( userID, 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( userID, 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 userID, int effectIndex ) { for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) { if( m_Channels[userID][i].in_use && m_Channels[userID][i].waveformIndex == effectIndex ) { m_Channels[userID][i].in_use = false; } } }
//---------------------------------------------------------
//---------------------------------------------------------
void CRumbleEffects::StopAllEffects( int userID ) { for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) { m_Channels[userID][i].in_use = false; }
m_flScreenShake[ userID ] = 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, left, right; fraction = elapsed - seconds;
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( int userID, float shake, float balance ) { if( m_bOutputEnabled ) { m_flScreenShake[ userID ] = shake; } else { // Silence
m_flScreenShake[ userID ] = 0.0f; } }
//---------------------------------------------------------
//---------------------------------------------------------
void CRumbleEffects::UpdateEffects( int userID, 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[userID][i];
if( pChannel->in_use ) { float left = 0, right = 0;
ComputeAmplitudes( pChannel, curtime, &left, &right ); fLeftMotor += left; fRightMotor += right; } }
// Add in any screenshake
float shakeLeft = 0.0f; float shakeRight = 0.0f; if( m_flScreenShake[ userID ] != 0.0f ) { if( m_flScreenShake[ userID ] < 0.0f ) { shakeLeft = fabs( m_flScreenShake[ userID ] ); } else { shakeRight = m_flScreenShake[ userID ]; } }
fLeftMotor += shakeLeft; fRightMotor += shakeRight;
fLeftMotor *= cl_rumblescale.GetFloat(); fRightMotor *= cl_rumblescale.GetFloat();
if ( engine->IsPaused() || !input->ControllerModeActive() ) { // Send nothing when paused or vibration disabled or not using controller.
fLeftMotor = 0.0f; fRightMotor = 0.0f; } inputsystem->SetRumble( fLeftMotor, fRightMotor, userID ); }
//---------------------------------------------------------
//---------------------------------------------------------
void StopAllRumbleEffects( int userID ) { // Kill all rumble channels that have effects assigned to this userID,
// and stop the motors.
g_RumbleEffects.StopAllEffects( userID );
inputsystem->StopRumble( userID ); }
//---------------------------------------------------------
//---------------------------------------------------------
void RumbleEffect( int userID, unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ) { g_RumbleEffects.StartEffect( userID, effectIndex, rumbleData, rumbleFlags ); }
//---------------------------------------------------------
//---------------------------------------------------------
void UpdateRumbleEffects( int userID ) {
// dkorus: maybe only stop weapon driven rumble effects?
// this was short circuiting the damage rumbles when being killed
C_BasePlayer *player = C_BasePlayer::GetLocalPlayer( XBX_GetSlotByUserId( userID ) );
if( !player ) { StopAllRumbleEffects( userID ); return; }
g_RumbleEffects.UpdateEffects( userID, gpGlobals->curtime ); }
//---------------------------------------------------------
//---------------------------------------------------------
void UpdateScreenShakeRumble( int userID, float shake, float balance ) { C_BasePlayer *player = C_BasePlayer::GetLocalPlayer( XBX_GetSlotByUserId( userID ) );
if( !player || !player->IsAlive() ) { return; }
g_RumbleEffects.UpdateScreenShakeRumble( userID, shake, balance ); }
//---------------------------------------------------------
//---------------------------------------------------------
void EnableRumbleOutput( bool bEnable ) { g_RumbleEffects.SetOutputEnabled( bEnable ); }
|