2500 lines
70 KiB
C++
2500 lines
70 KiB
C++
//=============================================================================
|
|
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
|
|
//
|
|
// File: vehiclesoundplayer.cpp
|
|
//
|
|
// Description: Implement VehicleSoundPlayer
|
|
//
|
|
// History: 30/06/2002 + Created -- Darren
|
|
//
|
|
//=============================================================================
|
|
|
|
//========================================
|
|
// System Includes
|
|
//========================================
|
|
#include <float.h>
|
|
|
|
#include <radnamespace.hpp>
|
|
|
|
//========================================
|
|
// Project Includes
|
|
//========================================
|
|
#include <sound/avatar/vehiclesoundplayer.h>
|
|
|
|
#include <sound/avatar/carsoundparameters.h>
|
|
#include <sound/tuning/globalsettings.h>
|
|
#include <sound/soundrenderer/soundrenderingmanager.h>
|
|
#include <sound/soundrenderer/idasoundresource.h>
|
|
#include <sound/soundrenderer/soundresourcemanager.h>
|
|
#include <sound/soundmanager.h>
|
|
|
|
#include <worldsim/redbrick/vehicle.h>
|
|
#include <worldsim/vehiclecentral.h>
|
|
#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
|
|
|
|
#include <input/button.h>
|
|
#include <events/eventmanager.h>
|
|
#include <mission/gameplaymanager.h>
|
|
|
|
|
|
|
|
//******************************************************************************
|
|
//
|
|
// Global Data, Local Data, Local Classes
|
|
//
|
|
//******************************************************************************
|
|
|
|
//
|
|
// For tuning
|
|
//
|
|
static bool s_resetEngineOnHonk = false;
|
|
static bool s_resetEngineInitialized = false;
|
|
|
|
static const char* s_skidResource = "skid";
|
|
static const char* s_gearResource = "gearshift";
|
|
|
|
static const float GAME_POWERSLIDE_TRIM_MIN = 0.6f;
|
|
static const float GAME_POWERSLIDE_TRIM_MAX = 0.9f;
|
|
|
|
static const float SUPERSPRINT_POWERSLIDE_TRIM_MIN = 0.4f;
|
|
static const float SUPERSPRINT_POWERSLIDE_TRIM_MAX = 0.7f;
|
|
|
|
//
|
|
// MACROS FOR DEBUG INFO
|
|
//
|
|
#ifdef SOUND_DEBUG_INFO_ENABLED
|
|
|
|
#define SET_SHIFT_IN_PROGRESS(inProgress) m_debugInfo->SetShiftInProgress(inProgress)
|
|
#define SET_CURRENT_GEAR(gear) m_debugInfo->SetCurrentGear(gear)
|
|
#define SET_CURRENT_SPEED(speed) m_debugInfo->SetCurrentSpeed(speed)
|
|
#define SET_SHIFT_TIME_REMAINING(time) m_debugInfo->SetShiftTime(time)
|
|
#define SET_DOWNSHIFT_SPEED(speed) m_debugInfo->SetDownshiftSpeed(speed)
|
|
#define SET_UPSHIFT_SPEED(speed) m_debugInfo->SetUpshiftSpeed(speed)
|
|
#define SET_CURRENT_PITCH(pitch) m_debugInfo->SetCurrentPitch(pitch)
|
|
#define SET_IS_DAMAGED(isDamaged) m_debugInfo->SetDamageEnabled(isDamaged)
|
|
#define SET_VEHICLE_LIFE(life) m_debugInfo->SetVehicleLife(life)
|
|
#define SET_DAMAGE_THRESHOLD(threshold) m_debugInfo->SetDamageThreshold(threshold)
|
|
|
|
//
|
|
// This is annoying. Stinky bad design.
|
|
//
|
|
#define SET_LOCAL_SHIFT_IN_PROGRESS(inProgress) m_debugInfo.SetShiftInProgress(inProgress)
|
|
#define SET_LOCAL_CURRENT_GEAR(gear) m_debugInfo.SetCurrentGear(gear)
|
|
#define SET_LOCAL_CURRENT_SPEED(speed) m_debugInfo.SetCurrentSpeed(speed)
|
|
#define SET_LOCAL_SHIFT_TIME_REMAINING(time) m_debugInfo.SetShiftTime(time)
|
|
#define SET_LOCAL_DOWNSHIFT_SPEED(speed) m_debugInfo.SetDownshiftSpeed(speed)
|
|
#define SET_LOCAL_UPSHIFT_SPEED(speed) m_debugInfo.SetUpshiftSpeed(speed)
|
|
#define SET_LOCAL_CURRENT_PITCH(pitch) m_debugInfo.SetCurrentPitch(pitch)
|
|
#define SET_LOCAL_IS_DAMAGED(isDamaged) m_debugInfo.SetDamageEnabled(isDamaged)
|
|
#define SET_LOCAL_VEHICLE_LIFE(life) m_debugInfo.SetVehicleLife(life)
|
|
#define SET_LOCAL_DAMAGE_THRESHOLD(threshold) m_debugInfo.SetDamageThreshold(threshold)
|
|
|
|
#else
|
|
|
|
#define SET_SHIFT_IN_PROGRESS(inProgress)
|
|
#define SET_CURRENT_GEAR(gear)
|
|
#define SET_CURRENT_SPEED(speed)
|
|
#define SET_SHIFT_TIME_REMAINING(time)
|
|
#define SET_DOWNSHIFT_SPEED(speed)
|
|
#define SET_UPSHIFT_SPEED(speed)
|
|
#define SET_CURRENT_PITCH(pitch)
|
|
#define SET_IS_DAMAGED(isDamaged)
|
|
#define SET_VEHICLE_LIFE(life)
|
|
#define SET_DAMAGE_THRESHOLD(threshold)
|
|
|
|
#define SET_LOCAL_SHIFT_IN_PROGRESS(inProgress)
|
|
#define SET_LOCAL_CURRENT_GEAR(gear)
|
|
#define SET_LOCAL_CURRENT_SPEED(speed)
|
|
#define SET_LOCAL_SHIFT_TIME_REMAINING(time)
|
|
#define SET_LOCAL_DOWNSHIFT_SPEED(speed)
|
|
#define SET_LOCAL_UPSHIFT_SPEED(speed)
|
|
#define SET_LOCAL_CURRENT_PITCH(pitch)
|
|
#define SET_LOCAL_IS_DAMAGED(isDamaged)
|
|
#define SET_LOCAL_VEHICLE_LIFE(life)
|
|
#define SET_LOCAL_DAMAGE_THRESHOLD(threshold)
|
|
|
|
#endif
|
|
|
|
enum GearShiftResult
|
|
{
|
|
GEARSHIFT_NONE,
|
|
GEARSHIFT_DAMPED,
|
|
GEARSHIFT_UP,
|
|
GEARSHIFT_DOWN
|
|
};
|
|
|
|
|
|
//
|
|
// Classes for engine states. Each one represents a state
|
|
// in which the engine will have different sound-speed characteristics
|
|
// (e.g. car in air, gearshift in progress).
|
|
//
|
|
|
|
//
|
|
// Base class for engine states
|
|
//
|
|
class EngineState
|
|
{
|
|
public:
|
|
EngineState();
|
|
virtual ~EngineState();
|
|
|
|
void Initialize( Vehicle* vehicle, carSoundParameters* parameters,
|
|
SimpsonsSoundPlayer* player, radKey32 resourceKey,
|
|
VehicleSoundDebugPage* debugPtr );
|
|
bool StartNewState() { return( m_nextState != ENGINE_STATE_INVALID ); }
|
|
EngineStateEnum GetNewState() { return( m_nextState ); }
|
|
void Reset() { m_nextState = ENGINE_STATE_INVALID; }
|
|
|
|
virtual void SetDebugInfo();
|
|
|
|
virtual void Service( unsigned int elapsedTime ) = 0;
|
|
|
|
virtual float GetCurrentPitch();
|
|
|
|
bool IsActive() { return( m_isActive ); }
|
|
void SetActive( bool isActive, float prevPitch = 0.0f );
|
|
|
|
void StartFade( float initialTrim, bool stopAfterFade = true );
|
|
bool IsFading();
|
|
void ServiceFade( unsigned int elapsedTime );
|
|
|
|
void SetTrim( float trim );
|
|
|
|
protected:
|
|
|
|
virtual void startup( float prevPitch ) = 0;
|
|
bool isAtIdleSpeed();
|
|
bool isSkidding();
|
|
|
|
//
|
|
// True if this state is running and producing sound
|
|
//
|
|
bool m_isActive;
|
|
|
|
//
|
|
// When we want to kick off a new state, set this to something
|
|
// other than ENGINE_STATE_INVALID
|
|
//
|
|
EngineStateEnum m_nextState;
|
|
|
|
//
|
|
// Stuff for getting vehicle and sound parameter info
|
|
//
|
|
Vehicle* m_vehicle;
|
|
carSoundParameters* m_parameters;
|
|
|
|
//
|
|
// Sound player stuff
|
|
//
|
|
SimpsonsSoundPlayer* m_player;
|
|
radKey32 m_resourceKey;
|
|
|
|
//
|
|
// Rev limiter, calculated from pitch ranges
|
|
//
|
|
float m_revLimit;
|
|
|
|
//
|
|
// Debug stuff
|
|
//
|
|
#ifdef SOUND_DEBUG_INFO_ENABLED
|
|
VehicleSoundDebugPage* m_debugInfo;
|
|
#endif
|
|
|
|
private:
|
|
//Prevent wasteful constructor creation.
|
|
EngineState( const EngineState& original );
|
|
EngineState& operator=( const EngineState& rhs );
|
|
|
|
float m_fadeTrim;
|
|
bool m_stopAfterFade;
|
|
};
|
|
|
|
//
|
|
// State for normal, on-ground, no-shift engine sound
|
|
//
|
|
class NormalEngineState : public EngineState
|
|
{
|
|
public:
|
|
NormalEngineState();
|
|
virtual ~NormalEngineState();
|
|
|
|
void Service( unsigned int elapsedTime );
|
|
|
|
void SetDebugInfo();
|
|
|
|
float GetCurrentPitch();
|
|
|
|
protected:
|
|
void startup( float prevPitch );
|
|
private:
|
|
//Prevent wasteful constructor creation.
|
|
NormalEngineState( const NormalEngineState& original );
|
|
NormalEngineState& operator=( const NormalEngineState& rhs );
|
|
|
|
GearShiftResult updateCurrentGearFromVehicle( bool dampenShifts = false );
|
|
|
|
int m_currentGear;
|
|
float m_currentSpeed;
|
|
float m_currentPitch;
|
|
bool m_isAttacking;
|
|
float m_currentTrim;
|
|
unsigned int m_interpolateTime;
|
|
};
|
|
|
|
//
|
|
// State for upshifts (e.g. 2nd to 3rd gear)
|
|
//
|
|
class UpshiftEngineState : public EngineState
|
|
{
|
|
public:
|
|
UpshiftEngineState();
|
|
virtual ~UpshiftEngineState();
|
|
|
|
void Service( unsigned int elapsedTime );
|
|
|
|
void SetDebugInfo();
|
|
|
|
protected:
|
|
void startup( float prevPitch );
|
|
private:
|
|
//Prevent wasteful constructor creation.
|
|
UpshiftEngineState( const UpshiftEngineState& original );
|
|
UpshiftEngineState& operator=( const UpshiftEngineState& rhs );
|
|
|
|
float m_startPitch;
|
|
float m_pitchDrop;
|
|
float m_shiftTimeMsecs;
|
|
unsigned int m_remainingTime;
|
|
};
|
|
|
|
//
|
|
// State for downshifts (e.g. 3rd to 2nd gear)
|
|
//
|
|
class DownshiftEngineState : public EngineState
|
|
{
|
|
public:
|
|
DownshiftEngineState();
|
|
virtual ~DownshiftEngineState();
|
|
|
|
void Service( unsigned int elapsedTime );
|
|
|
|
void SetDebugInfo();
|
|
|
|
float GetCurrentPitch();
|
|
|
|
protected:
|
|
void startup( float prevPitch );
|
|
private:
|
|
//Prevent wasteful constructor creation.
|
|
DownshiftEngineState( const DownshiftEngineState& original );
|
|
DownshiftEngineState& operator=( const DownshiftEngineState& rhs );
|
|
|
|
float m_startSpeed;
|
|
float m_startPitch;
|
|
float m_lastSpeed;
|
|
float m_currentPitch;
|
|
};
|
|
|
|
//
|
|
// State for car in mid-air
|
|
//
|
|
class InAirEngineState : public EngineState
|
|
{
|
|
public:
|
|
InAirEngineState();
|
|
virtual ~InAirEngineState();
|
|
|
|
void Service( unsigned int elapsedTime );
|
|
|
|
void SetDebugInfo();
|
|
|
|
protected:
|
|
void startup( float prevPitch );
|
|
private:
|
|
//Prevent wasteful constructor creation.
|
|
InAirEngineState( const InAirEngineState& original );
|
|
InAirEngineState& operator=( const InAirEngineState& rhs );
|
|
|
|
float m_currentPitch;
|
|
};
|
|
|
|
//
|
|
// State for car in reverse
|
|
//
|
|
class ReverseEngineState : public EngineState
|
|
{
|
|
public:
|
|
ReverseEngineState( SimpsonsSoundPlayer* beepPlayer );
|
|
virtual ~ReverseEngineState();
|
|
|
|
void Service( unsigned int elapsedTime );
|
|
|
|
void SetDebugInfo();
|
|
|
|
protected:
|
|
void startup( float prevPitch );
|
|
private:
|
|
//Prevent wasteful constructor creation.
|
|
ReverseEngineState( const ReverseEngineState& original );
|
|
ReverseEngineState& operator=( const ReverseEngineState& rhs );
|
|
|
|
SimpsonsSoundPlayer* m_beepPlayer;
|
|
};
|
|
|
|
//
|
|
// State for car in idle
|
|
//
|
|
class IdleEngineState : public EngineState
|
|
{
|
|
public:
|
|
IdleEngineState();
|
|
virtual ~IdleEngineState();
|
|
|
|
void Service( unsigned int elapsedTime );
|
|
|
|
void SetDebugInfo();
|
|
|
|
float GetCurrentPitch();
|
|
|
|
protected:
|
|
void startup( float prevPitch );
|
|
private:
|
|
//Prevent wasteful constructor creation.
|
|
IdleEngineState( const IdleEngineState& original );
|
|
IdleEngineState& operator=( const IdleEngineState& rhs );
|
|
|
|
float m_currentPitch;
|
|
};
|
|
|
|
//
|
|
// State for car when skidding
|
|
//
|
|
class SkidEngineState : public EngineState
|
|
{
|
|
public:
|
|
SkidEngineState();
|
|
virtual ~SkidEngineState();
|
|
|
|
void Service( unsigned int elapsedTime );
|
|
|
|
void SetDebugInfo();
|
|
|
|
float GetCurrentPitch();
|
|
|
|
protected:
|
|
void startup( float prevPitch );
|
|
|
|
private:
|
|
//Prevent wasteful constructor creation.
|
|
SkidEngineState( const SkidEngineState& original );
|
|
SkidEngineState& operator=( const SkidEngineState& rhs );
|
|
|
|
float m_currentPitch;
|
|
};
|
|
|
|
//******************************************************************************
|
|
//
|
|
// EngineState
|
|
//
|
|
//******************************************************************************
|
|
|
|
EngineState::EngineState() :
|
|
m_isActive( false ),
|
|
m_nextState( ENGINE_STATE_INVALID ),
|
|
m_vehicle( NULL ),
|
|
m_parameters( NULL ),
|
|
m_player( NULL ),
|
|
m_resourceKey( 0 ),
|
|
m_fadeTrim( 0.0f ),
|
|
m_stopAfterFade( true )
|
|
{
|
|
}
|
|
|
|
EngineState::~EngineState()
|
|
{
|
|
}
|
|
|
|
void EngineState::Initialize( Vehicle* vehicle, carSoundParameters* parameters,
|
|
SimpsonsSoundPlayer* player, radKey32 resourceKey,
|
|
VehicleSoundDebugPage* debugPtr )
|
|
{
|
|
rAssert( vehicle != NULL );
|
|
rAssert( parameters != NULL );
|
|
rAssert( player != NULL );
|
|
|
|
m_vehicle = vehicle;
|
|
m_parameters = parameters;
|
|
m_player = player;
|
|
m_resourceKey = resourceKey;
|
|
|
|
//
|
|
// Figure out a sensible rev limit
|
|
//
|
|
m_revLimit = parameters->GetRevLimit();
|
|
|
|
#ifdef SOUND_DEBUG_INFO_ENABLED
|
|
m_debugInfo = debugPtr;
|
|
#endif
|
|
}
|
|
|
|
void EngineState::SetActive( bool isActive, float prevPitch )
|
|
{
|
|
bool wasActive = m_isActive;
|
|
|
|
m_isActive = isActive;
|
|
if( m_isActive && !wasActive )
|
|
{
|
|
startup( prevPitch );
|
|
}
|
|
}
|
|
|
|
void EngineState::SetDebugInfo()
|
|
{
|
|
// Do nothing by default
|
|
}
|
|
|
|
bool EngineState::isAtIdleSpeed()
|
|
{
|
|
return( m_vehicle->GetSpeedKmh()
|
|
<= ( m_parameters->GetShiftPoint( 1 )
|
|
* m_vehicle->mDesignerParams.mDpTopSpeedKmh ) );
|
|
}
|
|
|
|
bool EngineState::isSkidding()
|
|
{
|
|
return( m_vehicle->GetSkidLevel() > 0.0f );
|
|
}
|
|
|
|
float EngineState::GetCurrentPitch()
|
|
{
|
|
//
|
|
// Not needed by all states, so return zero by default.
|
|
//
|
|
return( 0.0f );
|
|
}
|
|
|
|
bool EngineState::IsFading()
|
|
{
|
|
return( m_fadeTrim > 0.0f );
|
|
}
|
|
|
|
void EngineState::StartFade( float initialTrim, bool stopAfterFade )
|
|
{
|
|
m_fadeTrim = initialTrim;
|
|
m_stopAfterFade = stopAfterFade;
|
|
|
|
//
|
|
// Check whether we're already faded
|
|
//
|
|
if( ( m_fadeTrim <= 0.0f )
|
|
&& stopAfterFade )
|
|
{
|
|
m_player->Stop();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set the trim, in case the parent hasn't done it already
|
|
//
|
|
m_player->SetTrim( initialTrim );
|
|
}
|
|
}
|
|
|
|
void EngineState::ServiceFade( unsigned int elapsedTime )
|
|
{
|
|
//
|
|
// Fade from 1.0f to 0.0f in 1/10th of a second, time picked arbitrarily
|
|
//
|
|
m_fadeTrim -= static_cast<float>( elapsedTime ) / 100.0f;
|
|
if( m_fadeTrim < 0.0f )
|
|
{
|
|
m_fadeTrim = 0.0f;
|
|
}
|
|
|
|
m_player->SetTrim( m_fadeTrim );
|
|
|
|
if( ( m_fadeTrim == 0.0f )
|
|
&& m_stopAfterFade )
|
|
{
|
|
m_player->Stop();
|
|
}
|
|
}
|
|
|
|
void EngineState::SetTrim( float trim )
|
|
{
|
|
//
|
|
// Cancel fades in effect
|
|
//
|
|
m_fadeTrim = 0.0f;
|
|
|
|
m_player->SetTrim( trim );
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// NormalEngineState
|
|
//
|
|
//******************************************************************************
|
|
|
|
NormalEngineState::NormalEngineState() :
|
|
m_currentGear( 0 ),
|
|
m_currentSpeed( 0.0f ),
|
|
m_isAttacking( false ),
|
|
m_currentTrim( 1.0f ),
|
|
m_interpolateTime( 0 )
|
|
{
|
|
}
|
|
|
|
NormalEngineState::~NormalEngineState()
|
|
{
|
|
}
|
|
|
|
void NormalEngineState::Service( unsigned int elapsedTime )
|
|
{
|
|
GearShiftResult startedShift;
|
|
float newPitch;
|
|
float trimDiff;
|
|
float pitchDiff;
|
|
float maxDiff;
|
|
float speedPcnt;
|
|
float shiftPoint;
|
|
float shiftSpeed;
|
|
float topSpeed;
|
|
|
|
if( ( m_interpolateTime > 0 ) && !m_isAttacking )
|
|
{
|
|
//
|
|
// Delaying
|
|
//
|
|
if( elapsedTime > m_interpolateTime )
|
|
{
|
|
//
|
|
// Delay done, start attack
|
|
//
|
|
m_interpolateTime = static_cast<unsigned int>(m_parameters->GetAttackTimeMsecs());
|
|
m_isAttacking = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Delay not done
|
|
//
|
|
m_interpolateTime -= elapsedTime;
|
|
|
|
if( m_interpolateTime <= m_parameters->GetDelayTimeMsecs() )
|
|
{
|
|
m_currentTrim = m_parameters->GetDecayFinishTrim();
|
|
m_player->SetPitch( 1.0f );
|
|
}
|
|
|
|
//
|
|
// Since we're delaying, don't do anything else
|
|
//
|
|
return;
|
|
}
|
|
}
|
|
else if( m_interpolateTime > 0 )
|
|
{
|
|
//
|
|
// Attacking
|
|
//
|
|
if( elapsedTime > m_interpolateTime )
|
|
{
|
|
m_currentTrim = 1.0f;
|
|
m_interpolateTime = 0;
|
|
m_isAttacking = false;
|
|
}
|
|
else
|
|
{
|
|
m_interpolateTime -= elapsedTime;
|
|
trimDiff = static_cast<float>(elapsedTime) / m_parameters->GetAttackTimeMsecs();
|
|
m_currentTrim += trimDiff * ( 1.0f - m_parameters->GetDecayFinishTrim() ); // Increase at half-speed because we start at 0.5 above
|
|
if( m_currentTrim > 1.0f )
|
|
{
|
|
//
|
|
// Interpolation error
|
|
//
|
|
m_currentTrim = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Regular engine playback, make sure the attack is off and the trim is maxed.
|
|
//
|
|
m_isAttacking = false;
|
|
|
|
if( m_currentTrim != 1.0f )
|
|
{
|
|
m_currentTrim = 1.0f;
|
|
}
|
|
}
|
|
|
|
m_currentSpeed = m_vehicle->GetSpeedKmh();
|
|
|
|
if( m_vehicle->IsMovingBackward() )
|
|
{
|
|
m_nextState = ENGINE_STATE_REVERSE;
|
|
}
|
|
else if( m_vehicle->IsAirborn() )
|
|
{
|
|
m_nextState = ENGINE_STATE_IN_AIR;
|
|
}
|
|
else if( isSkidding() )
|
|
{
|
|
m_nextState = ENGINE_STATE_SKIDDING;
|
|
}
|
|
else
|
|
{
|
|
startedShift = updateCurrentGearFromVehicle();
|
|
if( ( startedShift == GEARSHIFT_UP )
|
|
|| ( startedShift == GEARSHIFT_DOWN ) )
|
|
{
|
|
if( startedShift == GEARSHIFT_UP )
|
|
{
|
|
m_nextState = ENGINE_STATE_UPSHIFTING;
|
|
//
|
|
// Don't stop the player, start delay
|
|
//
|
|
m_interpolateTime = m_parameters->GetDelayTimeMsecs()
|
|
+ static_cast<unsigned int>(m_parameters->GetDecayTimeMsecs());
|
|
if( m_interpolateTime > 0 )
|
|
{
|
|
m_currentTrim = 0.0f;
|
|
}
|
|
}
|
|
else if( m_currentGear > 0 )
|
|
{
|
|
m_nextState = ENGINE_STATE_DOWNSHIFTING;
|
|
}
|
|
else
|
|
{
|
|
m_nextState = ENGINE_STATE_IDLING;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( startedShift == GEARSHIFT_DAMPED )
|
|
{
|
|
//
|
|
// If we're going slower than the speed appropriate for the gear,
|
|
// don't let the pitch drop too low
|
|
//
|
|
shiftPoint = m_parameters->GetShiftPoint( m_currentGear );
|
|
topSpeed = m_vehicle->mDesignerParams.mDpTopSpeedKmh;
|
|
shiftSpeed = shiftPoint * topSpeed;
|
|
speedPcnt = m_currentSpeed / shiftSpeed;
|
|
newPitch = m_parameters->CalculateEnginePitch( m_currentGear, shiftSpeed, topSpeed );
|
|
newPitch *= speedPcnt;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No shift, just set the pitch to value appropriate to the vehicle speed
|
|
//
|
|
newPitch = m_parameters->CalculateEnginePitch( m_currentGear, m_currentSpeed,
|
|
m_vehicle->mDesignerParams.mDpTopSpeedKmh );
|
|
}
|
|
|
|
pitchDiff = newPitch - m_currentPitch;
|
|
maxDiff = static_cast<float>(elapsedTime) / m_parameters->GetMsecsPerOctaveCap();
|
|
|
|
//
|
|
// Ignore the calculated pitch if it means we're going to jump around
|
|
// too suddenly
|
|
//
|
|
if( pitchDiff >= 0.0f )
|
|
{
|
|
// Pitch rising
|
|
m_currentPitch += ( pitchDiff > maxDiff ) ? maxDiff : pitchDiff;
|
|
}
|
|
else
|
|
{
|
|
// Pitch dropping
|
|
m_currentPitch += ( pitchDiff < -maxDiff ) ? -maxDiff : pitchDiff;
|
|
}
|
|
|
|
if( m_currentPitch > m_revLimit )
|
|
{
|
|
m_currentPitch = m_revLimit;
|
|
}
|
|
|
|
m_player->SetPitch( m_currentPitch );
|
|
SET_CURRENT_PITCH( m_currentPitch );
|
|
|
|
if( !m_isAttacking )
|
|
{
|
|
m_currentTrim = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( m_nextState != ENGINE_STATE_INVALID )
|
|
{
|
|
if( ( m_nextState == ENGINE_STATE_DOWNSHIFTING )
|
|
|| ( m_nextState == ENGINE_STATE_UPSHIFTING ) )
|
|
{
|
|
StartFade( m_currentTrim, false );
|
|
}
|
|
else
|
|
{
|
|
SetTrim( m_currentTrim );
|
|
}
|
|
|
|
if( m_nextState != ENGINE_STATE_UPSHIFTING )
|
|
{
|
|
SetActive( false );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetTrim( m_currentTrim );
|
|
}
|
|
|
|
rAssertMsg( m_currentPitch >= 0.0f, "If you're running a debugger, tell Esan about this" );
|
|
}
|
|
|
|
void NormalEngineState::SetDebugInfo()
|
|
{
|
|
if( IsActive() )
|
|
{
|
|
SET_CURRENT_GEAR( m_currentGear );
|
|
SET_CURRENT_SPEED( m_currentSpeed );
|
|
}
|
|
}
|
|
|
|
float NormalEngineState::GetCurrentPitch()
|
|
{
|
|
return( m_currentPitch );
|
|
}
|
|
|
|
void NormalEngineState::startup( float prevPitch )
|
|
{
|
|
m_currentSpeed = m_vehicle->GetSpeedKmh();
|
|
m_currentGear = m_parameters->CalculateCurrentGear( m_vehicle->GetSpeedKmh(), m_currentSpeed,
|
|
m_vehicle->mDesignerParams.mDpTopSpeedKmh,
|
|
-1 );
|
|
|
|
//
|
|
// Start the sound
|
|
//
|
|
if( !(m_player->IsInUse()) )
|
|
{
|
|
m_player->PlaySound( m_resourceKey );
|
|
}
|
|
|
|
m_currentTrim = 1.0f;
|
|
m_interpolateTime = 0;
|
|
if( prevPitch > 0.0f )
|
|
{
|
|
m_currentPitch = prevPitch;
|
|
}
|
|
else
|
|
{
|
|
m_currentPitch = m_parameters->CalculateEnginePitch( m_currentGear, m_currentSpeed,
|
|
m_vehicle->mDesignerParams.mDpTopSpeedKmh );
|
|
}
|
|
SetTrim( m_currentTrim );
|
|
|
|
if( m_currentPitch > m_revLimit )
|
|
{
|
|
m_currentPitch = m_revLimit;
|
|
}
|
|
m_player->SetPitch( m_currentPitch );
|
|
}
|
|
|
|
//=============================================================================
|
|
// NormalEngineState::updateCurrentGearFromVehicle
|
|
//=============================================================================
|
|
// Description: Update m_currentGear from the current state of m_vehicle
|
|
//
|
|
// Parameters: dampenShifts - indicates whether we want to eliminate unnecessary
|
|
// gear shifts. Not needed when we get in the car.
|
|
//
|
|
// Return: enumeration indicating if we shifted and if so, which direction
|
|
//
|
|
//=============================================================================
|
|
GearShiftResult NormalEngineState::updateCurrentGearFromVehicle( bool dampenShifts /* = false */ )
|
|
{
|
|
int currentGear;
|
|
GearShiftResult shiftResult;
|
|
float damperSize;
|
|
float upperShiftSpeed;
|
|
int oldGear = m_currentGear;
|
|
float topSpeed = m_vehicle->mDesignerParams.mDpTopSpeedKmh;
|
|
|
|
if( dampenShifts )
|
|
{
|
|
currentGear = m_currentGear;
|
|
}
|
|
else
|
|
{
|
|
currentGear = -1;
|
|
}
|
|
|
|
m_currentGear = m_parameters->CalculateCurrentGear( m_vehicle->GetSpeedKmh(), m_currentSpeed,
|
|
topSpeed, -1 );
|
|
|
|
if( m_currentGear == oldGear )
|
|
{
|
|
shiftResult = GEARSHIFT_NONE;
|
|
}
|
|
else
|
|
{
|
|
if( m_currentGear > oldGear )
|
|
{
|
|
if( m_vehicle->IsSafeToUpShift() )
|
|
{
|
|
shiftResult = GEARSHIFT_UP;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't upshift when we're climbing hills, because the engine
|
|
// tends to stay bogged down at low revs
|
|
//
|
|
m_currentGear = oldGear;
|
|
shiftResult = GEARSHIFT_NONE;
|
|
}
|
|
}
|
|
else if( m_currentGear == 0 )
|
|
{
|
|
//
|
|
// Don't damp going into idle
|
|
//
|
|
shiftResult = GEARSHIFT_DOWN;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Apply some gearshift damping
|
|
//
|
|
damperSize = m_parameters->GetDownshiftDamperSize() * topSpeed;
|
|
upperShiftSpeed = m_parameters->GetShiftPoint( m_currentGear + 1 ) * topSpeed;
|
|
if( ( upperShiftSpeed - m_currentSpeed ) <= damperSize )
|
|
{
|
|
shiftResult = GEARSHIFT_DAMPED;
|
|
|
|
//
|
|
// Wah. I think there's a problem where having the top gear too narrow can
|
|
// cause us to bump up into a non-existent gear. Don't do that.
|
|
//
|
|
if( upperShiftSpeed < topSpeed )
|
|
{
|
|
++m_currentGear;
|
|
}
|
|
//else
|
|
//{
|
|
// rDebugString("Gotcha!");
|
|
//}
|
|
}
|
|
else
|
|
{
|
|
shiftResult = GEARSHIFT_DOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
return( shiftResult );
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// UpshiftEngineState
|
|
//
|
|
//******************************************************************************
|
|
|
|
UpshiftEngineState::UpshiftEngineState()
|
|
{
|
|
}
|
|
|
|
UpshiftEngineState::~UpshiftEngineState()
|
|
{
|
|
}
|
|
|
|
void UpshiftEngineState::Service( unsigned int elapsedTime )
|
|
{
|
|
float newPitch;
|
|
float timePercent;
|
|
float newTrim;
|
|
float targetTrim;
|
|
|
|
if( elapsedTime > m_remainingTime )
|
|
{
|
|
//
|
|
// Upshift is done, return to normal engine sound
|
|
//
|
|
|
|
//
|
|
// No need for state change, normal doesn't shut off on
|
|
// upshifts anymore
|
|
//
|
|
//m_nextState = ENGINE_STATE_NORMAL;
|
|
|
|
SetTrim( 1.0f );
|
|
m_player->Stop();
|
|
SetActive( false );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Calculate the amount we need to drop the pitch in this frame
|
|
//
|
|
m_remainingTime -= elapsedTime;
|
|
timePercent = static_cast<float>(m_remainingTime) / static_cast<float>(m_shiftTimeMsecs);
|
|
newPitch = m_startPitch - ( ( 1.0f - timePercent ) * m_pitchDrop );
|
|
targetTrim = m_parameters->GetDecayFinishTrim();
|
|
newTrim = targetTrim + ( ( 1.0f - targetTrim ) * timePercent );
|
|
|
|
m_player->SetPitch( newPitch );
|
|
SET_CURRENT_PITCH( newPitch );
|
|
SetTrim( newTrim );
|
|
}
|
|
}
|
|
|
|
void UpshiftEngineState::SetDebugInfo()
|
|
{
|
|
if( IsActive() )
|
|
{
|
|
SET_SHIFT_IN_PROGRESS( true );
|
|
SET_SHIFT_TIME_REMAINING( m_remainingTime );
|
|
SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
|
|
}
|
|
}
|
|
|
|
void UpshiftEngineState::startup( float prevPitch )
|
|
{
|
|
int currentGear;
|
|
float currentSpeed;
|
|
|
|
//
|
|
// Initialize the current pitch, the amount we're going to drop it by,
|
|
// and the length of time we'll take to drop it
|
|
//
|
|
currentSpeed = m_vehicle->GetSpeedKmh();
|
|
currentGear = m_parameters->CalculateCurrentGear( currentSpeed, currentSpeed,
|
|
m_vehicle->mDesignerParams.mDpTopSpeedKmh,
|
|
-1 );
|
|
|
|
//
|
|
// Don't forget to subtract one from the gear because the parameter calculation
|
|
// will already have signalled the upshift, and we still want the old gear's pitch
|
|
//
|
|
m_startPitch = m_parameters->CalculateEnginePitch( currentGear - 1, currentSpeed,
|
|
m_vehicle->mDesignerParams.mDpTopSpeedKmh );
|
|
m_pitchDrop = m_parameters->GetGearShiftPitchDrop( currentGear );
|
|
m_shiftTimeMsecs = m_parameters->GetDecayTimeMsecs();
|
|
m_remainingTime = static_cast<unsigned int>(m_shiftTimeMsecs);
|
|
|
|
//
|
|
// Start the player
|
|
//
|
|
if( !(m_player->IsInUse()) )
|
|
{
|
|
m_player->PlaySound( m_resourceKey );
|
|
}
|
|
m_player->SetPitch( m_startPitch );
|
|
SetTrim( 1.0f );
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// DownshiftEngineState
|
|
//
|
|
//******************************************************************************
|
|
|
|
DownshiftEngineState::DownshiftEngineState()
|
|
{
|
|
}
|
|
|
|
DownshiftEngineState::~DownshiftEngineState()
|
|
{
|
|
}
|
|
|
|
void DownshiftEngineState::Service( unsigned int elapsedTime )
|
|
{
|
|
float speedPercent;
|
|
float idlePitch;
|
|
float currentSpeed = m_vehicle->GetSpeedKmh();
|
|
|
|
if( isAtIdleSpeed() )
|
|
{
|
|
//
|
|
// Switch to idle sound
|
|
//
|
|
m_nextState = ENGINE_STATE_IDLING;
|
|
|
|
m_player->Stop();
|
|
SetActive( false );
|
|
}
|
|
else if( currentSpeed > m_lastSpeed )
|
|
{
|
|
//
|
|
// We've started accelerating again, switch to normal engine sound
|
|
//
|
|
m_nextState = ENGINE_STATE_NORMAL;
|
|
|
|
StartFade( 1.0f );
|
|
SetActive( false );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Calculate new engine pitch
|
|
//
|
|
speedPercent = m_vehicle->GetSpeedKmh() / m_startSpeed;
|
|
// For now, use idle engine pitch for bottom of pitch range
|
|
idlePitch = m_parameters->GetIdleEnginePitch();
|
|
m_currentPitch = idlePitch + ( speedPercent * ( m_startPitch - idlePitch ) );
|
|
|
|
m_player->SetPitch( m_currentPitch );
|
|
SET_CURRENT_PITCH( m_currentPitch );
|
|
|
|
m_lastSpeed = currentSpeed;
|
|
}
|
|
}
|
|
|
|
void DownshiftEngineState::SetDebugInfo()
|
|
{
|
|
if( IsActive() )
|
|
{
|
|
SET_SHIFT_IN_PROGRESS( true );
|
|
SET_SHIFT_TIME_REMAINING( 0 );
|
|
SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
|
|
}
|
|
}
|
|
|
|
float DownshiftEngineState::GetCurrentPitch()
|
|
{
|
|
return( m_currentPitch );
|
|
}
|
|
|
|
void DownshiftEngineState::startup( float prevPitch )
|
|
{
|
|
//int currentGear;
|
|
|
|
m_startSpeed = m_vehicle->GetSpeedKmh();
|
|
m_lastSpeed = m_startSpeed;
|
|
|
|
//
|
|
// Find the speed that we need to interpolate over
|
|
//
|
|
|
|
// "+ 1" is because we're already in the new gear, and we want to interpolate
|
|
// from the last one
|
|
//currentGear = m_parameters->CalculateCurrentGear( m_startSpeed, m_startSpeed,
|
|
// m_vehicle->mDesignerParams.mDpTopSpeedKmh,
|
|
// -1 ) + 1;
|
|
|
|
//m_startPitch = m_parameters->CalculateEnginePitch( currentGear, m_startSpeed,
|
|
// m_vehicle->mDesignerParams.mDpTopSpeedKmh );
|
|
m_startPitch = prevPitch;
|
|
m_currentPitch = m_startPitch;
|
|
|
|
//
|
|
// Start the sound
|
|
//
|
|
if( !(m_player->IsInUse()) )
|
|
{
|
|
m_player->PlaySound( m_resourceKey );
|
|
}
|
|
m_player->SetPitch( m_startPitch );
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// InAirEngineState
|
|
//
|
|
//******************************************************************************
|
|
|
|
InAirEngineState::InAirEngineState()
|
|
{
|
|
}
|
|
|
|
InAirEngineState::~InAirEngineState()
|
|
{
|
|
}
|
|
|
|
void InAirEngineState::Service( unsigned int elapsedTime )
|
|
{
|
|
float maxPitchChange;
|
|
float idlePitch;
|
|
float throttlePitch;
|
|
unsigned int responseTime;
|
|
float targetPitch;
|
|
float newPitch;
|
|
|
|
if( !(m_vehicle->IsAirborn()) )
|
|
{
|
|
if( isAtIdleSpeed() )
|
|
{
|
|
m_nextState = ENGINE_STATE_IDLING;
|
|
}
|
|
else if( isSkidding() )
|
|
{
|
|
m_nextState = ENGINE_STATE_SKIDDING;
|
|
}
|
|
else
|
|
{
|
|
m_nextState = ENGINE_STATE_NORMAL;
|
|
}
|
|
|
|
m_player->Stop();
|
|
SetActive( false );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Calculate the maximum pitch change given the car's responsiveness
|
|
idlePitch = m_parameters->GetInAirIdlePitch();
|
|
throttlePitch = m_parameters->GetInAirThrottlePitch();
|
|
responseTime = m_parameters->GetInAirThrottleResponseTimeMsecs();
|
|
maxPitchChange = ( throttlePitch - idlePitch )
|
|
* ( static_cast<float>(elapsedTime) / static_cast<float>(responseTime) );
|
|
|
|
//
|
|
// Get target pitch
|
|
//
|
|
targetPitch = idlePitch + ( m_vehicle->mGas * ( throttlePitch - idlePitch ) );
|
|
if( targetPitch > m_currentPitch )
|
|
{
|
|
if( m_currentPitch + maxPitchChange > targetPitch )
|
|
{
|
|
newPitch = targetPitch;
|
|
}
|
|
else
|
|
{
|
|
newPitch = m_currentPitch + maxPitchChange;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_currentPitch - maxPitchChange < targetPitch )
|
|
{
|
|
newPitch = targetPitch;
|
|
}
|
|
else
|
|
{
|
|
newPitch = m_currentPitch - maxPitchChange;
|
|
}
|
|
}
|
|
|
|
m_player->SetPitch( newPitch );
|
|
m_currentPitch = newPitch;
|
|
}
|
|
}
|
|
|
|
void InAirEngineState::SetDebugInfo()
|
|
{
|
|
if( IsActive() )
|
|
{
|
|
SET_CURRENT_PITCH( m_currentPitch );
|
|
SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
|
|
}
|
|
}
|
|
|
|
void InAirEngineState::startup( float prevPitch )
|
|
{
|
|
if( !(m_player->IsInUse()) )
|
|
{
|
|
m_player->PlaySound( m_resourceKey );
|
|
}
|
|
|
|
m_currentPitch = prevPitch;
|
|
|
|
m_player->SetPitch( m_currentPitch );
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// ReverseEngineState
|
|
//
|
|
//******************************************************************************
|
|
|
|
ReverseEngineState::ReverseEngineState( SimpsonsSoundPlayer* beepPlayer ) :
|
|
m_beepPlayer( beepPlayer )
|
|
{
|
|
}
|
|
|
|
ReverseEngineState::~ReverseEngineState()
|
|
{
|
|
}
|
|
|
|
void ReverseEngineState::Service( unsigned int elapsedTime )
|
|
{
|
|
float speed;
|
|
float maxReverseSpeed;
|
|
float minPitch;
|
|
float maxPitch;
|
|
float newPitch;
|
|
|
|
speed = m_vehicle->GetSpeedKmh();
|
|
|
|
if( !(m_vehicle->IsMovingBackward()) )
|
|
{
|
|
if( m_vehicle->IsAirborn() )
|
|
{
|
|
m_nextState = ENGINE_STATE_IN_AIR;
|
|
}
|
|
else if( isAtIdleSpeed() )
|
|
{
|
|
m_nextState = ENGINE_STATE_IDLING;
|
|
}
|
|
else if( isSkidding() )
|
|
{
|
|
m_nextState = ENGINE_STATE_SKIDDING;
|
|
}
|
|
else
|
|
{
|
|
m_nextState = ENGINE_STATE_NORMAL;
|
|
}
|
|
|
|
m_beepPlayer->Stop();
|
|
SetActive( false );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set pitch based on speed
|
|
//
|
|
maxReverseSpeed = m_parameters->GetReversePitchCapKmh();
|
|
m_parameters->GetReversePitchRange( minPitch, maxPitch );
|
|
if( speed > maxReverseSpeed )
|
|
{
|
|
newPitch = maxPitch;
|
|
}
|
|
else
|
|
{
|
|
newPitch = minPitch + ( ( speed / maxReverseSpeed ) * ( maxPitch - minPitch ) );
|
|
}
|
|
|
|
m_player->SetPitch( newPitch );
|
|
SET_CURRENT_PITCH( newPitch );
|
|
}
|
|
}
|
|
|
|
void ReverseEngineState::SetDebugInfo()
|
|
{
|
|
if( IsActive() )
|
|
{
|
|
SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
|
|
SET_CURRENT_GEAR( -1 );
|
|
}
|
|
}
|
|
|
|
void ReverseEngineState::startup( float prevPitch )
|
|
{
|
|
const char* backupClipName;
|
|
|
|
if( !(m_player->IsInUse()) )
|
|
{
|
|
m_player->PlaySound( m_resourceKey );
|
|
}
|
|
|
|
//
|
|
// Call Service() to set the pitch
|
|
//
|
|
Service( 0 );
|
|
|
|
//
|
|
// If we have a backup beep sound, play it
|
|
//
|
|
if( ( backupClipName = m_parameters->GetBackupClipName() ) != NULL )
|
|
{
|
|
m_beepPlayer->PlaySound( backupClipName );
|
|
}
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// IdleEngineState
|
|
//
|
|
//******************************************************************************
|
|
|
|
IdleEngineState::IdleEngineState() :
|
|
m_currentPitch( 0.0f )
|
|
{
|
|
}
|
|
|
|
IdleEngineState::~IdleEngineState()
|
|
{
|
|
}
|
|
|
|
void IdleEngineState::Service( unsigned int elapsedTime )
|
|
{
|
|
float idlePitch;
|
|
float pitchDiff;
|
|
float maxDiff;
|
|
|
|
//
|
|
// Check for car motion, indicating we're leaving the idle state
|
|
//
|
|
if( m_vehicle->IsMovingBackward() )
|
|
{
|
|
m_nextState = ENGINE_STATE_REVERSE;
|
|
}
|
|
else if( isSkidding() )
|
|
{
|
|
m_nextState = ENGINE_STATE_SKIDDING;
|
|
}
|
|
else if( !isAtIdleSpeed() )
|
|
{
|
|
m_nextState = ENGINE_STATE_NORMAL;
|
|
}
|
|
|
|
if( m_nextState != ENGINE_STATE_INVALID )
|
|
{
|
|
//
|
|
// Deactivate self
|
|
//
|
|
m_player->Stop();
|
|
SetActive( false );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Keep playing idle. Interpolate to it if we're not playing
|
|
// the idle pitch already
|
|
//
|
|
idlePitch = m_parameters->GetIdleEnginePitch();
|
|
if( m_currentPitch != idlePitch )
|
|
{
|
|
pitchDiff = idlePitch - m_currentPitch;
|
|
maxDiff = static_cast<float>(elapsedTime) / m_parameters->GetMsecsPerOctaveCap();
|
|
|
|
//
|
|
// Ignore the calculated pitch if it means we're going to jump around
|
|
// too suddenly
|
|
//
|
|
if( pitchDiff >= 0.0f )
|
|
{
|
|
// Pitch rising
|
|
m_currentPitch += ( pitchDiff > maxDiff ) ? maxDiff : pitchDiff;
|
|
}
|
|
else
|
|
{
|
|
// Pitch dropping
|
|
m_currentPitch += ( pitchDiff < -maxDiff ) ? -maxDiff : pitchDiff;
|
|
}
|
|
|
|
m_player->SetPitch( m_currentPitch );
|
|
SET_CURRENT_PITCH( m_currentPitch );
|
|
}
|
|
}
|
|
}
|
|
|
|
void IdleEngineState::SetDebugInfo()
|
|
{
|
|
if( IsActive() )
|
|
{
|
|
SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
|
|
SET_CURRENT_GEAR( 0 );
|
|
}
|
|
}
|
|
|
|
float IdleEngineState::GetCurrentPitch()
|
|
{
|
|
return( m_currentPitch );
|
|
}
|
|
|
|
void IdleEngineState::startup( float prevPitch )
|
|
{
|
|
rAssert( m_parameters != NULL );
|
|
|
|
if( !(m_player->IsInUse()) )
|
|
{
|
|
m_player->PlaySound( m_resourceKey );
|
|
}
|
|
|
|
m_currentPitch = m_parameters->GetIdleEnginePitch();
|
|
if( prevPitch != m_currentPitch )
|
|
{
|
|
m_currentPitch = prevPitch;
|
|
}
|
|
|
|
m_player->SetPitch( m_currentPitch );
|
|
SET_CURRENT_PITCH( m_currentPitch );
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// SkidEngineState
|
|
//
|
|
//******************************************************************************
|
|
|
|
SkidEngineState::SkidEngineState() :
|
|
m_currentPitch( 0.0f )
|
|
{
|
|
}
|
|
|
|
SkidEngineState::~SkidEngineState()
|
|
{
|
|
}
|
|
|
|
void SkidEngineState::Service( unsigned int elapsedTime )
|
|
{
|
|
float burnoutLevel;
|
|
float desiredPitch;
|
|
float minBurnoutPitch;
|
|
float minPowerslidePitch;
|
|
float pitchDiff;
|
|
float maxDiff;
|
|
|
|
//
|
|
// Get a desired pitch based on the burnout level that the
|
|
// vehicle object gives us
|
|
//
|
|
if( isSkidding() )
|
|
{
|
|
//
|
|
// Sometimes we're skidding, sometimes we're burning out. Try to figure out
|
|
// which value makes more sense.
|
|
//
|
|
if( m_vehicle->mBurnoutLevel > 0.0f )
|
|
{
|
|
burnoutLevel = m_vehicle->mBurnoutLevel;
|
|
|
|
minBurnoutPitch = m_parameters->GetBurnoutMinPitch();
|
|
desiredPitch = minBurnoutPitch +
|
|
( ( m_parameters->GetBurnoutMaxPitch() - minBurnoutPitch )
|
|
* burnoutLevel );
|
|
}
|
|
else
|
|
{
|
|
//burnoutLevel = m_vehicle->GetSkidLevel();
|
|
|
|
//
|
|
// The skid level appears to be a constant value of 0.5. Try using the
|
|
// vehicle gas value instead, since the tires would be spinning kinda
|
|
// freely in the real world anyway.
|
|
//
|
|
// HACK: Since sliding skids seem to be quite different from burnouts,
|
|
// bump the burnout level beyond 1.0f at will to make this sound right.
|
|
// This should be a separate set of tuning parameters next time.
|
|
//
|
|
burnoutLevel = m_vehicle->mGas;
|
|
|
|
minPowerslidePitch = m_parameters->GetPowerslideMinPitch();
|
|
desiredPitch = minPowerslidePitch +
|
|
( ( m_parameters->GetPowerslideMaxPitch() - minPowerslidePitch )
|
|
* burnoutLevel );
|
|
}
|
|
|
|
//
|
|
// Ease toward desired pitch
|
|
//
|
|
pitchDiff = desiredPitch - m_currentPitch;
|
|
maxDiff = static_cast<float>(elapsedTime) / ( m_parameters->GetMsecsPerOctaveCap() * 4 );
|
|
|
|
//
|
|
// Ignore the calculated pitch if it means we're going to jump around
|
|
// too suddenly
|
|
//
|
|
if( pitchDiff >= 0.0f )
|
|
{
|
|
// Pitch rising
|
|
m_currentPitch += ( pitchDiff > maxDiff ) ? maxDiff : pitchDiff;
|
|
}
|
|
else
|
|
{
|
|
// Pitch dropping
|
|
m_currentPitch += ( pitchDiff < -maxDiff ) ? -maxDiff : pitchDiff;
|
|
}
|
|
|
|
m_player->SetPitch( m_currentPitch );
|
|
SET_CURRENT_PITCH( m_currentPitch );
|
|
}
|
|
else
|
|
{
|
|
if( isAtIdleSpeed() )
|
|
{
|
|
m_nextState = ENGINE_STATE_IDLING;
|
|
}
|
|
else if( m_vehicle->IsMovingBackward() )
|
|
{
|
|
m_nextState = ENGINE_STATE_REVERSE;
|
|
}
|
|
else
|
|
{
|
|
m_nextState = ENGINE_STATE_NORMAL;
|
|
}
|
|
|
|
SetActive( false );
|
|
}
|
|
}
|
|
|
|
void SkidEngineState::SetDebugInfo()
|
|
{
|
|
if( IsActive() )
|
|
{
|
|
SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
|
|
}
|
|
}
|
|
|
|
float SkidEngineState::GetCurrentPitch()
|
|
{
|
|
return( m_currentPitch );
|
|
}
|
|
|
|
void SkidEngineState::startup( float prevPitch )
|
|
{
|
|
rAssert( m_parameters != NULL );
|
|
|
|
if( !(m_player->IsInUse()) )
|
|
{
|
|
m_player->PlaySound( m_resourceKey );
|
|
}
|
|
|
|
m_currentPitch = prevPitch;
|
|
|
|
m_player->SetPitch( m_currentPitch );
|
|
SET_CURRENT_PITCH( m_currentPitch );
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// VehicleSoundPlayer
|
|
//
|
|
//******************************************************************************
|
|
|
|
//******************************************************************************
|
|
//
|
|
// Public Member Functions
|
|
//
|
|
//******************************************************************************
|
|
|
|
//==============================================================================
|
|
// VehicleSoundPlayer::VehicleSoundPlayer
|
|
//==============================================================================
|
|
// Description: Constructor.
|
|
//
|
|
// Parameters: None.
|
|
//
|
|
// Return: N/A.
|
|
//
|
|
//==============================================================================
|
|
VehicleSoundPlayer::VehicleSoundPlayer() :
|
|
#ifdef SOUND_DEBUG_INFO_ENABLED
|
|
m_debugInfo( 2, GetSoundManager()->GetDebugDisplay() ),
|
|
#endif
|
|
m_vehicle( NULL ),
|
|
m_parameters( NULL ),
|
|
m_peeloutSettings( NULL ),
|
|
m_isSkidding( false ),
|
|
m_hornPlaying( false ),
|
|
m_oneTimeHorn( false ),
|
|
m_powerslideTrim( 0.0f ),
|
|
m_playingDamage( false ),
|
|
m_proximityAIVehicle( NULL ),
|
|
m_terrainType( TT_Road )
|
|
{
|
|
m_engineStates[ENGINE_STATE_NORMAL] = new NormalEngineState();
|
|
m_engineStates[ENGINE_STATE_UPSHIFTING] = new UpshiftEngineState();
|
|
m_engineStates[ENGINE_STATE_DOWNSHIFTING] = new DownshiftEngineState();
|
|
m_engineStates[ENGINE_STATE_IN_AIR] = new InAirEngineState();
|
|
m_engineStates[ENGINE_STATE_REVERSE] = new ReverseEngineState( &(m_soundPlayers[CARPLAYER_BACKUP_BEEP]) );
|
|
m_engineStates[ENGINE_STATE_IDLING] = new IdleEngineState();
|
|
m_engineStates[ENGINE_STATE_SKIDDING] = new SkidEngineState();
|
|
|
|
if( !s_resetEngineInitialized )
|
|
{
|
|
radDbgWatchAddBoolean( &s_resetEngineOnHonk, "Reset engine on honk", "Sound Info" );
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
// VehicleSoundPlayer::~VehicleSoundPlayer
|
|
//==============================================================================
|
|
// Description: Destructor.
|
|
//
|
|
// Parameters: None.
|
|
//
|
|
// Return: N/A.
|
|
//
|
|
//==============================================================================
|
|
VehicleSoundPlayer::~VehicleSoundPlayer()
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < NUM_ENGINE_STATES; i++ )
|
|
{
|
|
delete m_engineStates[i];
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::StartCarSounds
|
|
//=============================================================================
|
|
// Description: Begin playing car-related sounds
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::StartCarSounds( Vehicle* newVehicle )
|
|
{
|
|
IRadNameSpace* nameSpace;
|
|
IRefCount* nameSpaceObj;
|
|
VehicleSoundDebugPage* debugPtr;
|
|
const char* overlayName;
|
|
|
|
rAssert( newVehicle != NULL );
|
|
|
|
GetEventManager()->TriggerEvent( EVENT_AVATAR_VEHICLE_TOGGLE, newVehicle );
|
|
|
|
//
|
|
// Don't bother switching sounds if we're already playing this car
|
|
//
|
|
if( ( newVehicle == m_vehicle ) && carSoundIsActive() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Remember which vehicle we're playing
|
|
//
|
|
m_vehicle = newVehicle;
|
|
|
|
//
|
|
// Get the car sound parameter object for this vehicle.
|
|
//
|
|
// IMPORTANT: We assume that the object in the namespace has the same
|
|
// name as the one in the vehicle object, which comes from the loading
|
|
// script for that car, I think.
|
|
//
|
|
nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
|
|
rAssert( nameSpace != NULL );
|
|
|
|
nameSpaceObj = nameSpace->GetInstance( m_vehicle->GetName() );
|
|
if( nameSpaceObj != NULL )
|
|
{
|
|
m_parameters = reinterpret_cast<carSoundParameters*>( nameSpaceObj );
|
|
|
|
//
|
|
// Set up the Watcher tuning
|
|
//
|
|
m_parameters->SetWatcherName( m_vehicle->GetName() );
|
|
|
|
//
|
|
// Get global car values
|
|
//
|
|
nameSpaceObj = nameSpace->GetInstance( "tuner" );
|
|
rAssert( nameSpaceObj != NULL );
|
|
m_peeloutSettings = reinterpret_cast<globalSettings*>( nameSpaceObj );
|
|
|
|
const char* clipName = m_parameters->GetEngineClipName();
|
|
|
|
const char* idleClipName = m_parameters->GetEngineIdleClipName();
|
|
if( idleClipName == NULL )
|
|
{
|
|
//
|
|
// No idle clip, use the engine clip by default
|
|
//
|
|
idleClipName = clipName;
|
|
}
|
|
|
|
if( ( clipName != NULL ) && ( idleClipName != NULL ) )
|
|
{
|
|
#ifdef SOUND_DEBUG_INFO_ENABLED
|
|
debugPtr = &m_debugInfo;
|
|
#else
|
|
debugPtr = NULL;
|
|
#endif
|
|
|
|
//
|
|
// Initialize the engine sound states
|
|
//
|
|
m_engineStates[ENGINE_STATE_NORMAL]->Initialize( newVehicle, m_parameters,
|
|
&m_soundPlayers[CARPLAYER_ENGINE],
|
|
::radMakeKey32( clipName ),
|
|
debugPtr );
|
|
m_engineStates[ENGINE_STATE_UPSHIFTING]->Initialize( newVehicle, m_parameters,
|
|
&m_soundPlayers[CARPLAYER_SHIFT],
|
|
::radMakeKey32( clipName ),
|
|
debugPtr );
|
|
m_engineStates[ENGINE_STATE_DOWNSHIFTING]->Initialize( newVehicle, m_parameters,
|
|
&m_soundPlayers[CARPLAYER_SHIFT],
|
|
::radMakeKey32( clipName ),
|
|
debugPtr );
|
|
m_engineStates[ENGINE_STATE_IN_AIR]->Initialize( newVehicle, m_parameters,
|
|
&m_soundPlayers[CARPLAYER_ENGINE],
|
|
::radMakeKey32( clipName ),
|
|
debugPtr );
|
|
m_engineStates[ENGINE_STATE_REVERSE]->Initialize( newVehicle, m_parameters,
|
|
&m_soundPlayers[CARPLAYER_ENGINE],
|
|
::radMakeKey32( clipName ),
|
|
debugPtr );
|
|
m_engineStates[ENGINE_STATE_IDLING]->Initialize( newVehicle, m_parameters,
|
|
&m_soundPlayers[CARPLAYER_ENGINE],
|
|
::radMakeKey32( idleClipName ),
|
|
debugPtr );
|
|
m_engineStates[ENGINE_STATE_SKIDDING]->Initialize( newVehicle, m_parameters,
|
|
&m_soundPlayers[CARPLAYER_ENGINE],
|
|
::radMakeKey32( clipName ),
|
|
debugPtr );
|
|
|
|
//
|
|
// Assume that we're getting into an idle car
|
|
//
|
|
m_engineStates[ENGINE_STATE_IDLING]->SetActive( true );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// What the...? No engine clip? That's not right.
|
|
//
|
|
rTuneString( "WARNING: No engine clip found. No engine sounds will be played.\n" );
|
|
m_vehicle = NULL;
|
|
}
|
|
|
|
//
|
|
// If we have an overlay clip, start it
|
|
//
|
|
overlayName = m_parameters->GetOverlayClipName();
|
|
if( overlayName != NULL )
|
|
{
|
|
m_soundPlayers[CARPLAYER_OVERLAY].PlaySound( overlayName );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rTunePrintf( "Couldn't find carSoundParameters object named %s, no sound played\n",
|
|
m_vehicle->GetName() );
|
|
m_vehicle = NULL;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::StopCarSounds
|
|
//=============================================================================
|
|
// Description: Stop playing car-related sounds
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::StopCarSounds()
|
|
{
|
|
unsigned int i;
|
|
|
|
for( i = 0; i < CARPLAYER_NUMPLAYERS; i++ )
|
|
{
|
|
m_soundPlayers[i].Stop();
|
|
}
|
|
|
|
for( i = 0; i < NUM_ENGINE_STATES; i++ )
|
|
{
|
|
m_engineStates[i]->SetActive( false );
|
|
m_engineStates[i]->Reset();
|
|
}
|
|
|
|
m_proximityAIVehicle = NULL;
|
|
|
|
GetEventManager()->TriggerEvent( EVENT_AVATAR_VEHICLE_TOGGLE, m_vehicle );
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::UpdateSoundParameters
|
|
//=============================================================================
|
|
// Description: Look at the car sound parameter object, and make sure that
|
|
// the engine sound is being played at a pitch appropriate for
|
|
// the vehicle RPMs.
|
|
//
|
|
// Parameters: elapsedTime - time since last frame in msecs
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::UpdateSoundParameters( unsigned int elapsedTime )
|
|
{
|
|
//float newPitch;
|
|
//unsigned int shiftTime;
|
|
int i;
|
|
static bool warningPrinted = false;
|
|
bool activateState[NUM_ENGINE_STATES];
|
|
float prevStatePitch[NUM_ENGINE_STATES];
|
|
EngineStateEnum newState;
|
|
|
|
//
|
|
// Before updating, check to make sure we have a vehicle. If we don't,
|
|
// then we got into a car that didn't have a carSoundParameters object
|
|
// associated with it. Handle it gracefully, but we really should fix it
|
|
//
|
|
if( m_vehicle != NULL )
|
|
{
|
|
for( i = 0; i < NUM_ENGINE_STATES; i++ )
|
|
{
|
|
activateState[i] = false;
|
|
prevStatePitch[i] = 0.0f;
|
|
}
|
|
|
|
//
|
|
// Service the active engine states
|
|
//
|
|
for( i = 0; i < NUM_ENGINE_STATES; i++ )
|
|
{
|
|
if( m_engineStates[i]->IsActive() )
|
|
{
|
|
m_engineStates[i]->Service( elapsedTime );
|
|
if( m_engineStates[i]->StartNewState() )
|
|
{
|
|
newState = m_engineStates[i]->GetNewState();
|
|
activateState[newState] = true;
|
|
prevStatePitch[newState] = m_engineStates[i]->GetCurrentPitch();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Service fades
|
|
//
|
|
for( i = 0; i < NUM_ENGINE_STATES; i++ )
|
|
{
|
|
if( m_engineStates[i]->IsFading() )
|
|
{
|
|
m_engineStates[i]->ServiceFade( elapsedTime );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize debug info
|
|
//
|
|
SET_LOCAL_SHIFT_IN_PROGRESS( false );
|
|
|
|
//
|
|
// Start up any states that were marked for activation during servicing
|
|
//
|
|
for( i = 0; i < NUM_ENGINE_STATES; i++ )
|
|
{
|
|
if( activateState[i] )
|
|
{
|
|
m_engineStates[i]->SetActive( true, prevStatePitch[i] );
|
|
}
|
|
|
|
m_engineStates[i]->Reset();
|
|
|
|
m_engineStates[i]->SetDebugInfo();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( !warningPrinted )
|
|
{
|
|
rTuneString("Warning: Vehicle doesn't have a sound associated with it (CarSoundParameters)\n");
|
|
warningPrinted = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::UpdateOncePerFrame
|
|
//=============================================================================
|
|
// Description: Update routine, used to set the engine RPMs correctly and
|
|
// check for skids, gear changes, and horns
|
|
//
|
|
// Parameters: elapsedTime - time since last frame in msecs
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::UpdateOncePerFrame( unsigned int elapsedTime )
|
|
{
|
|
UpdateSoundParameters( elapsedTime );
|
|
checkDamage();
|
|
CheckForSkid( elapsedTime );
|
|
CheckHorn();
|
|
checkProximity();
|
|
|
|
//
|
|
// Debug stuff
|
|
//
|
|
if( ( m_parameters != NULL ) && ( m_vehicle != NULL ) )
|
|
{
|
|
/*SET_SHIFT_IN_PROGRESS( m_gearChangeInProgress );
|
|
SET_CURRENT_GEAR( m_currentGear );
|
|
SET_CURRENT_SPEED( m_currentSpeed );
|
|
SET_SHIFT_TIME_REMAINING( m_gearChangeTimeRemaining );
|
|
SET_DOWNSHIFT_SPEED( m_parameters->GetLowShiftPoint( m_currentGear ) * m_vehicle->mDesignerParams.mDpTopSpeedKmh );
|
|
SET_UPSHIFT_SPEED( m_parameters->GetHighShiftPoint( m_currentGear ) * m_vehicle->mDesignerParams.mDpTopSpeedKmh );*/
|
|
SET_LOCAL_IS_DAMAGED( m_playingDamage );
|
|
SET_LOCAL_VEHICLE_LIFE( m_vehicle->GetVehicleLifePercentage(m_vehicle->mHitPoints) );
|
|
SET_LOCAL_DAMAGE_THRESHOLD( m_parameters->GetDamageStartPcnt() );
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::CheckForSkid
|
|
//=============================================================================
|
|
// Description: Check the vehicle object to see if we're skidding, and make
|
|
// the appropriate noise
|
|
//
|
|
// Parameters: elapsedTime - time elapsed in the last frame
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::CheckForSkid( unsigned int elapsedTime )
|
|
{
|
|
bool skidPlaySuccessful;
|
|
float trim;
|
|
float skidLevel;
|
|
float peeloutMin;
|
|
float peeloutMax;
|
|
const char* skidName;
|
|
bool isSupersprint;
|
|
float trimMax;
|
|
|
|
//
|
|
// If we don't have a car, don't do anything
|
|
//
|
|
if( m_vehicle == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Find out if the car is skidding
|
|
//
|
|
skidLevel = m_vehicle->GetSkidLevel();
|
|
peeloutMin = m_peeloutSettings->GetPeeloutMin();
|
|
peeloutMax = m_peeloutSettings->GetPeeloutMax();
|
|
if( skidLevel > peeloutMin )
|
|
{
|
|
//
|
|
// Vehicle skidding. Start making tire squeal if we're not doing it yet
|
|
//
|
|
if( !m_isSkidding )
|
|
{
|
|
//
|
|
// Get the terrain type underneath the car so we know which sound
|
|
// resource to play
|
|
//
|
|
m_terrainType = m_vehicle->mTerrainType;
|
|
skidName = getSkidResourceForTerrain( m_terrainType );
|
|
|
|
//
|
|
// Squeal like a pig, boy
|
|
//
|
|
skidPlaySuccessful =
|
|
m_soundPlayers[CARPLAYER_SKID].PlaySound( skidName );
|
|
rAssert( skidPlaySuccessful );
|
|
|
|
m_isSkidding = true;
|
|
m_powerslideTrim = 0.0f;
|
|
|
|
//
|
|
// If we're starting from a slow speed, assume this is a burnout
|
|
//
|
|
|
|
// move the triggering, and triggering of the END of this event, to
|
|
// Vehicle since Cary wants it also, to play with the camera
|
|
|
|
//if( m_vehicle->GetSpeedKmh() < 10.0f )
|
|
//{
|
|
// GetEventManager()->TriggerEvent( EVENT_BURNOUT );
|
|
//}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check for whether we've changed terrain types
|
|
//
|
|
if( m_vehicle->mTerrainType != m_terrainType )
|
|
{
|
|
m_terrainType = m_vehicle->mTerrainType;
|
|
skidName = getSkidResourceForTerrain( m_terrainType );
|
|
|
|
m_soundPlayers[CARPLAYER_SKID].Stop();
|
|
|
|
skidPlaySuccessful =
|
|
m_soundPlayers[CARPLAYER_SKID].PlaySound( skidName );
|
|
rAssert( skidPlaySuccessful );
|
|
}
|
|
}
|
|
|
|
if( m_vehicle->mBurnoutLevel > 0.0f )
|
|
{
|
|
//
|
|
// Burnout
|
|
//
|
|
if( skidLevel >= peeloutMax )
|
|
{
|
|
trim = m_peeloutSettings->GetPeeloutMaxTrim();
|
|
}
|
|
else
|
|
{
|
|
trim = ( ( skidLevel - peeloutMin ) / ( peeloutMax - peeloutMin ) )
|
|
* m_peeloutSettings->GetPeeloutMaxTrim();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Powerslide
|
|
//
|
|
if( ( GetGameplayManager() != NULL )
|
|
&& ( GetGameplayManager()->IsSuperSprint() ) )
|
|
{
|
|
isSupersprint = true;
|
|
trimMax = SUPERSPRINT_POWERSLIDE_TRIM_MAX;
|
|
}
|
|
else
|
|
{
|
|
isSupersprint = false;
|
|
trimMax = GAME_POWERSLIDE_TRIM_MAX;
|
|
}
|
|
|
|
if( m_powerslideTrim <= 0.0f )
|
|
{
|
|
if( isSupersprint )
|
|
{
|
|
m_powerslideTrim = SUPERSPRINT_POWERSLIDE_TRIM_MIN;
|
|
}
|
|
else
|
|
{
|
|
m_powerslideTrim = GAME_POWERSLIDE_TRIM_MIN;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_powerslideTrim += static_cast<float>(elapsedTime) * 0.0003f;
|
|
}
|
|
|
|
if( m_powerslideTrim > trimMax )
|
|
{
|
|
m_powerslideTrim = trimMax;
|
|
}
|
|
|
|
trim = m_powerslideTrim;
|
|
}
|
|
m_soundPlayers[CARPLAYER_SKID].SetTrim( trim );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not skidding. Stop the noise if it's playing
|
|
//
|
|
if( m_isSkidding )
|
|
{
|
|
m_soundPlayers[CARPLAYER_SKID].Stop();
|
|
m_isSkidding = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::CheckHorn
|
|
//=============================================================================
|
|
// Description: Find the vehicle controller, and check to see whether the horn
|
|
// button is being pressed and play/stop/do nothing with the
|
|
// horn clip appropriately
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::CheckHorn()
|
|
{
|
|
int vehicleId;
|
|
VehicleController* controller;
|
|
bool hornPlaySuccessful;
|
|
float buttonVal = 0.0f;
|
|
const char* hornClipName;
|
|
IDaSoundResource* resource;
|
|
|
|
if( m_vehicle == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Find the horn button for this vehicle and get its state
|
|
//
|
|
vehicleId = GetVehicleCentral()->GetVehicleId( m_vehicle, false );
|
|
controller = GetVehicleCentral()->GetVehicleController( vehicleId );
|
|
|
|
//
|
|
// Controller might already be gone, if we're playing car sound while
|
|
// the player is getting out of the car.
|
|
//
|
|
if( controller != NULL )
|
|
{
|
|
buttonVal = controller->GetHorn();
|
|
}
|
|
|
|
//
|
|
// Look for whether we need to start/stop a sound
|
|
//
|
|
if( buttonVal > 0.05f )
|
|
{
|
|
if( !m_hornPlaying )
|
|
{
|
|
//
|
|
// Horn button has just been pressed, find the correct clip and play it
|
|
//
|
|
rAssert( m_parameters != NULL );
|
|
hornClipName = m_parameters->GetHornClipName();
|
|
|
|
if( hornClipName == NULL )
|
|
{
|
|
hornClipName = "horn";
|
|
}
|
|
|
|
//
|
|
// Just in case we were still playing a non-looping horn sound
|
|
//
|
|
m_soundPlayers[CARPLAYER_HORNPLAYER].Stop();
|
|
|
|
hornPlaySuccessful =
|
|
m_soundPlayers[CARPLAYER_HORNPLAYER].PlaySound( hornClipName );
|
|
rAssert( hornPlaySuccessful );
|
|
|
|
m_hornPlaying = true;
|
|
|
|
//
|
|
// Find out if this is looping horn sound
|
|
//
|
|
resource = Sound::daSoundRenderingManagerGet()->GetResourceManager()->FindResource( hornClipName );
|
|
rAssert( resource != NULL );
|
|
|
|
// Sanity check
|
|
if( resource == NULL )
|
|
{
|
|
rTunePrintf( "No horn named %s found\n", hornClipName );
|
|
m_oneTimeHorn = true;
|
|
}
|
|
else
|
|
{
|
|
m_oneTimeHorn = !( resource->GetLooping() );
|
|
}
|
|
|
|
#ifndef RAD_RELEASE
|
|
//
|
|
// Tuning hack. Stop and restart the engine to pick up any trim changes
|
|
//
|
|
if( s_resetEngineOnHonk )
|
|
{
|
|
Vehicle* theCar = m_vehicle;
|
|
StopCarSounds();
|
|
StartCarSounds( theCar );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_hornPlaying )
|
|
{
|
|
//
|
|
// Horn button has just been released, stop the clip
|
|
//
|
|
if( !m_oneTimeHorn )
|
|
{
|
|
m_soundPlayers[CARPLAYER_HORNPLAYER].Stop();
|
|
}
|
|
m_hornPlaying = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::OnPlaybackComplete
|
|
//=============================================================================
|
|
// Description: SimpsonsSoundPlayer callback, used when gearshift is done
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::OnPlaybackComplete()
|
|
{
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::OnSoundReady
|
|
//=============================================================================
|
|
// Description: Unused, required for SimpsonsSoundPlayerCallback interface
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::OnSoundReady()
|
|
{
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::AddAIVehicleProximityTest
|
|
//=============================================================================
|
|
// Description: Add check for proximity to newly spawned AI vehicle
|
|
//
|
|
// Parameters: aiVehicle - vehicle to test
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::AddAIVehicleProximityTest( Vehicle* aiVehicle )
|
|
{
|
|
if( m_proximityAIVehicle == NULL )
|
|
{
|
|
m_proximityAIVehicle = aiVehicle;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::DeleteAIVehicleProximityTest
|
|
//=============================================================================
|
|
// Description: Stop proximity testing for given vehicle, if that's the one
|
|
// we're currently testing.
|
|
//
|
|
// Parameters: aiVehicle - vehicle to stop testing for
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::DeleteAIVehicleProximityTest( Vehicle* aiVehicle )
|
|
{
|
|
if( m_proximityAIVehicle == aiVehicle )
|
|
{
|
|
m_proximityAIVehicle = NULL;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::PlayDoorOpen
|
|
//=============================================================================
|
|
// Description: Play a door opening sound
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::PlayDoorOpen()
|
|
{
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::PlayDoorClose
|
|
//=============================================================================
|
|
// Description: Play a door closing sound
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::PlayDoorClose()
|
|
{
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// Private Member Functions
|
|
//
|
|
//******************************************************************************
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::checkDamage
|
|
//=============================================================================
|
|
// Description: Start or stop the damage sound for this vehicle as appropriate
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::checkDamage()
|
|
{
|
|
float damagePercentage;
|
|
float lifePercentage;
|
|
float damageTrimPercentage;
|
|
float damageTrim;
|
|
float trimRange;
|
|
float minTrim;
|
|
|
|
if( m_vehicle == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
damagePercentage = m_parameters->GetDamageStartPcnt();
|
|
lifePercentage = m_vehicle->GetVehicleLifePercentage(m_vehicle->mHitPoints);
|
|
minTrim = m_parameters->GetDamageStartTrim();
|
|
trimRange = m_parameters->GetDamageMaxTrim() - m_parameters->GetDamageStartTrim();
|
|
|
|
if( ( lifePercentage <= damagePercentage ) && ( !m_playingDamage ) )
|
|
{
|
|
m_playingDamage = true;
|
|
m_soundPlayers[CARPLAYER_DAMAGE].PlaySound( m_parameters->GetDamagedEngineClipName() );
|
|
|
|
//
|
|
// Scale volume by amount of damage
|
|
//
|
|
damageTrimPercentage = ( damagePercentage - lifePercentage ) / m_parameters->GetDamageVolumeRange();
|
|
if( damageTrimPercentage > 1.0f )
|
|
{
|
|
damageTrimPercentage = 1.0f;
|
|
}
|
|
damageTrim = minTrim + ( damageTrimPercentage * trimRange );
|
|
m_soundPlayers[CARPLAYER_DAMAGE].SetTrim( damageTrim );
|
|
}
|
|
else if( m_playingDamage )
|
|
{
|
|
if( lifePercentage > damagePercentage )
|
|
{
|
|
//
|
|
// Must've reset or something
|
|
//
|
|
m_playingDamage = false;
|
|
m_soundPlayers[CARPLAYER_DAMAGE].Stop();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Adjust volume
|
|
//
|
|
damageTrimPercentage = ( damagePercentage - lifePercentage ) / m_parameters->GetDamageVolumeRange();
|
|
if( damageTrimPercentage > 1.0f )
|
|
{
|
|
damageTrimPercentage = 1.0f;
|
|
}
|
|
damageTrim = minTrim + ( damageTrimPercentage * trimRange );
|
|
m_soundPlayers[CARPLAYER_DAMAGE].SetTrim( damageTrim );
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::carSoundIsActive
|
|
//=============================================================================
|
|
// Description: Indicate whether any of the engine sound states are going
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: true if a state is active, false otherwise
|
|
//
|
|
//=============================================================================
|
|
bool VehicleSoundPlayer::carSoundIsActive()
|
|
{
|
|
int i;
|
|
bool isActive = false;
|
|
|
|
for( i = 0; i < NUM_ENGINE_STATES; i++ )
|
|
{
|
|
if( m_engineStates[i]->IsActive() )
|
|
{
|
|
isActive = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return( isActive );
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::checkProximity
|
|
//=============================================================================
|
|
// Description: Determine whether AI vehicle has gotten close enough to
|
|
// trigger an event
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Return: void
|
|
//
|
|
//=============================================================================
|
|
void VehicleSoundPlayer::checkProximity()
|
|
{
|
|
rmt::Vector position1;
|
|
rmt::Vector position2;
|
|
|
|
//rAssert( m_vehicle != NULL );
|
|
|
|
if( m_proximityAIVehicle != NULL && m_vehicle != NULL )
|
|
{
|
|
m_vehicle->GetPosition( &position1 );
|
|
m_proximityAIVehicle->GetPosition( &position2 );
|
|
|
|
//
|
|
// Trigger event at 20 (use 20^2 for efficiency) metres
|
|
//
|
|
position1.Sub( position2 );
|
|
if( position1.MagnitudeSqr() < 400.0f )
|
|
{
|
|
GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_PROXIMITY );
|
|
m_proximityAIVehicle = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// VehicleSoundPlayer::getSkidResourceForTerrain
|
|
//=============================================================================
|
|
// Description: Find the name of the sound resource for a skid appropriate
|
|
// for the kind of ground we're driving over
|
|
//
|
|
// Parameters: terrain - type of terrain the car is over right now
|
|
//
|
|
// Return: name of sound resource to use
|
|
//
|
|
//=============================================================================
|
|
const char* VehicleSoundPlayer::getSkidResourceForTerrain( eTerrainType terrain )
|
|
{
|
|
IRadNameSpace* nameSpace;
|
|
IRefCount* nameSpaceObj;
|
|
globalSettings* clipNameObj;
|
|
const char* name;
|
|
bool terrainIsDirt = ( terrain == TT_Dirt )
|
|
|| ( terrain == TT_Sand )
|
|
|| ( terrain == TT_Gravel )
|
|
|| ( terrain == TT_Grass );
|
|
|
|
//
|
|
// First, see if we have something specific for this car
|
|
//
|
|
rAssert( m_parameters != NULL );
|
|
if( terrainIsDirt )
|
|
{
|
|
name = m_parameters->GetDirtSkidClipName();
|
|
}
|
|
else
|
|
{
|
|
name = m_parameters->GetRoadSkidClipName();
|
|
}
|
|
|
|
if( name == NULL )
|
|
{
|
|
//
|
|
// No car-specific skid, get the global one from the globalSettings
|
|
// object
|
|
//
|
|
nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
|
|
rAssert( nameSpace != NULL );
|
|
nameSpaceObj = nameSpace->GetInstance( "tuner" );
|
|
rAssert( nameSpaceObj != NULL );
|
|
|
|
clipNameObj = static_cast<globalSettings*>( nameSpaceObj );
|
|
|
|
if( terrainIsDirt )
|
|
{
|
|
name = clipNameObj->GetSkidDirtClipName();
|
|
}
|
|
else
|
|
{
|
|
name = clipNameObj->GetSkidRoadClipName();
|
|
}
|
|
}
|
|
|
|
rAssert( name != NULL );
|
|
|
|
return( name );
|
|
}
|