3055 lines
108 KiB
C++
3055 lines
108 KiB
C++
|
|
#include "vehiclecollisionaudio.h"
|
|
#include "boataudioentity.h"
|
|
#include "frontendaudioentity.h"
|
|
#include "debugaudio.h"
|
|
#include "dooraudioentity.h"
|
|
#include "northaudioengine.h"
|
|
#include "audio/environment/environment.h"
|
|
#include "audio/vehiclereflectionsaudioentity.h"
|
|
#include "pedaudioentity.h"
|
|
#include "traileraudioentity.h"
|
|
#include "caraudioentity.h"
|
|
#include "policescanner.h"
|
|
#include "radioaudioentity.h"
|
|
#include "radiostation.h"
|
|
#include "radioslot.h"
|
|
#include "weatheraudioentity.h"
|
|
#include "trainaudioentity.h"
|
|
|
|
#include "animation/animbones.h"
|
|
#include "audioeffecttypes/waveshapereffect.h"
|
|
#include "audioengine/categorymanager.h"
|
|
#include "audioengine/engine.h"
|
|
#include "audioengine/engineutil.h"
|
|
#include "audioengine/environment.h"
|
|
#include "audioengine/environment_game.h"
|
|
#include "audioengine/soundfactory.h"
|
|
#include "audioengine/widgets.h"
|
|
#include "audiohardware/device.h"
|
|
#include "audiohardware/driver.h"
|
|
#include "audiohardware/driverdefs.h"
|
|
#include "audiohardware/driverutil.h"
|
|
#include "audiosoundtypes/envelopesound.h"
|
|
#include "audiosoundtypes/simplesound.h"
|
|
#include "audiosoundtypes/sound.h"
|
|
#include "audiosoundtypes/soundcontrol.h"
|
|
#include "audiosoundtypes/sounddefs.h"
|
|
#include "atl/staticpool.h"
|
|
#include "camera/CamInterface.h"
|
|
#include "camera/cinematic/CinematicDirector.h"
|
|
#include "camera/cinematic/camera/tracking/CinematicHeliChaseCamera.h"
|
|
#include "camera/gameplay/GameplayDirector.h"
|
|
#include "camera/gameplay/follow/FollowVehicleCamera.h"
|
|
#include "vehicleAi/vehicleintelligence.h"
|
|
#include "control/record.h"
|
|
#include "control/replay/audio/CollisionAudioPacket.h"
|
|
#include "crskeleton/skeleton.h"
|
|
#include "debug/debugglobals.h"
|
|
#include "grcore/debugdraw.h"
|
|
#include "game/ModelIndices.h"
|
|
#include "game/weather.h"
|
|
#include "modelinfo/vehiclemodelinfo.h"
|
|
#include "network/NetworkInterface.h"
|
|
#include "phbound/boundcomposite.h"
|
|
#include "physics/gtaMaterialManager.h"
|
|
#include "physics/physics.h"
|
|
#include "profile/element.h"
|
|
#include "objects/Door.h"
|
|
#include "renderer/Water.h"
|
|
#include "fwsys/timer.h"
|
|
#include "vehicles/automobile.h"
|
|
#include "vehicles/boat.h"
|
|
#include "vehicles/door.h"
|
|
#include "vehicles/heli.h"
|
|
#include "vehicles/vehicle.h"
|
|
#include "vehicles/Trailer.h"
|
|
#include "vehicles/AmphibiousAutomobile.h"
|
|
#include "vfx/Systems/VfxVehicle.h"
|
|
#include "peds/pedeventscanner.h"
|
|
#include "peds/PopCycle.h"
|
|
#include "peds/PedIntelligence.h"
|
|
#include "peds/PlayerPed.h"
|
|
#include "Task/Vehicle/TaskEnterVehicle.h"
|
|
#include "Task/Vehicle/TaskExitVehicle.h"
|
|
#include "vfx/systems/VfxWheel.h"
|
|
#include "renderer/water.h"
|
|
|
|
AUDIO_VEHICLES_OPTIMISATIONS()
|
|
|
|
|
|
extern float g_CarVelForPedImpact;
|
|
extern float g_BodyDamageFactorForDebris;
|
|
extern float g_BodyDamageFactorForHydraulicDebris;
|
|
extern float g_DamageImpactVel;
|
|
extern float g_TrailerBumpVel;
|
|
|
|
extern f32 g_MinCarRollImpulseMag;
|
|
extern f32 g_MinCarRollSpeed;
|
|
extern f32 g_MaxCarRollImpulseMag;
|
|
extern f32 g_MaxCarRollSpeed;
|
|
extern u32 g_HydraulicsJumpLandSuppressionTime;
|
|
|
|
extern fwPtrListSingleLink g_RecentlyCollidedMaterials;
|
|
extern u32 g_CollisionMaterialCountThreshold;
|
|
extern bool g_UseCollisionIntensityCulling;
|
|
extern bool g_PedsMakeSolidCollisions;
|
|
|
|
f32 g_BoatBottomContactDotThreshold = 0.4f;
|
|
u32 g_BlockingTimeForFakeImpacts = 300;
|
|
u32 g_BlockingTimeForVehicleCollisions = 100;
|
|
u32 g_StuntJumpLandingMinJumpTimeMs = 500;
|
|
u32 g_ToyCarMinAirTimeForBigLanding = 250;
|
|
u32 g_ToyCarMinBigJumpLandDuration = 500;
|
|
u32 g_ToyCarMinSmallJumpLandDuration = 150;
|
|
bool g_NoVehicleCollisions = false;
|
|
bool g_NoFakeVehicleCollisions = false;
|
|
bool g_AlwaysPlayFakeCollisions = false;
|
|
bool g_NoDeformationAudio = false;
|
|
bool g_NoCrashSweetener = false;
|
|
bool g_NoHeadlightSmashAudio = false;
|
|
bool g_NoVehicleScrapeAudio = false;
|
|
bool g_ApplyBoatBodyCollisionLimitingToAllBoats = false;
|
|
|
|
extern CVfxWheel g_vfxWheel;
|
|
|
|
namespace rage
|
|
{
|
|
#if __BANK
|
|
EXT_PF_COUNTER(CollisonsProcessed);
|
|
#endif
|
|
}
|
|
|
|
extern u32 g_MinTimeBetweenCollisions;
|
|
float g_VehCollisionJumpLandMag = 0.f;
|
|
u32 g_jumpLandTimeFilter = 100;
|
|
u32 g_jumpLandScrapeTimeFilter = 250;
|
|
float g_DeformationImpactVel = 5.f;
|
|
float g_VehicleExplosionCollisionAudioVolumeBoost = 6.f;
|
|
float g_VehicleExplosionCollisionAudioRolloffBoost = 10.f;
|
|
u32 g_VehicleExplosionAudioBoostTime = 5000;
|
|
f32 g_VehicleSpeedSqForFoliage = 1.f;
|
|
u32 g_VehicleRollHoldTime = 500;
|
|
#if __BANK
|
|
bool audVehicleCollisionAudio::sm_UpdateVehicleCollisionSettings = false;
|
|
bool g_DebugVehicleCollisionAudio = false;
|
|
extern bool g_DebugHydraulicSounds;
|
|
#endif
|
|
|
|
const u32 g_DefaultVehRoll = ATSTRINGHASH("CAR_ROLL_SOUND_temp", 0x0ea4f5504);
|
|
|
|
audSoundSet audVehicleCollisionAudio::sm_SoundSet;
|
|
|
|
|
|
audCurve audVehicleCollisionAudio::sm_VehicleLandFactorCurve;
|
|
audCurve audVehicleCollisionAudio::sm_VehicleCollisionRolloffBoostCurve;
|
|
u32 audVehicleCollisionAudio::sm_TrainLoopTime = 100;
|
|
float audVehicleCollisionAudio::sm_MinSpeedForVehicleCollisions = 0.5f;
|
|
u32 audVehicleCollisionAudio::sm_TimeForBikePostMeleeImpacts = 3000;
|
|
|
|
atStaticPool<audVehicleCollisionContext, audVehicleCollisionContextList::k_MaxCollisionEvents> sm_ContextPool ;
|
|
|
|
#if !__FINAL
|
|
u32 audVehicleCollisionAudio::sm_debugEventCounter = 0;
|
|
bool audVehicleCollisionAudio::sm_debugEventListFull = false;
|
|
#endif
|
|
|
|
audVehicleCollisionContextList::audVehicleCollisionContextList()
|
|
{
|
|
m_ReuseList = NULL;
|
|
m_List = NULL;
|
|
m_FrameCount = 0;
|
|
}
|
|
|
|
void audVehicleCollisionContextList::Reset()
|
|
{
|
|
audVehicleCollisionContext * listEvent = m_List, *nextEvent = NULL;
|
|
m_List = NULL;
|
|
|
|
while(listEvent)
|
|
{
|
|
if(listEvent->otherEntity)
|
|
listEvent->otherEntity->m_nFlags.bAlreadyInAudioList = 0;
|
|
|
|
nextEvent = listEvent->next;
|
|
sm_ContextPool.Delete(listEvent);
|
|
|
|
listEvent = nextEvent;
|
|
}
|
|
|
|
// Always clear out the reuse list too in this case.
|
|
ClearReuseList();
|
|
}
|
|
|
|
|
|
void audVehicleCollisionContextList::ResetToReuseList()
|
|
{
|
|
// Special case: if the actual list is already empty, don't do anything, in
|
|
// particular, don't clear the reuse list. This is handy if
|
|
// ResetToReuseList() is called multiple times.
|
|
if(!m_List)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Clear anything already on the reuse list from before, we only want it
|
|
// to match what's currently in m_List.
|
|
ClearReuseList();
|
|
|
|
audVehicleCollisionContext * listEvent = m_List, *nextEvent = NULL;
|
|
m_List = NULL;
|
|
|
|
// Clear bAlreadyInAudioList for everything in the main list, and push
|
|
// on m_ReuseList. Note that we intentionally reverse the order here, so
|
|
// that when we grab from m_ReuseList again, we will end up with the nodes
|
|
// in the same order as they had in m_List before.
|
|
while(listEvent)
|
|
{
|
|
if(listEvent->otherEntity)
|
|
listEvent->otherEntity->m_nFlags.bAlreadyInAudioList = 0;
|
|
|
|
nextEvent = listEvent->next;
|
|
|
|
listEvent->next = m_ReuseList;
|
|
m_ReuseList = listEvent;
|
|
|
|
listEvent = nextEvent;
|
|
}
|
|
}
|
|
|
|
|
|
void audVehicleCollisionContextList::ClearReuseList()
|
|
{
|
|
audVehicleCollisionContext * listEvent = m_ReuseList, *nextEvent = NULL;
|
|
m_ReuseList = NULL;
|
|
|
|
while(listEvent)
|
|
{
|
|
// When we return to the pool, we should probably clear out the registered references,
|
|
// so we don't hold on to extra fwRefAwareBase objects that other users may have
|
|
// to iterate through.
|
|
listEvent->otherEntity = NULL;
|
|
#if !__FINAL
|
|
listEvent->parent = NULL;
|
|
#endif
|
|
|
|
nextEvent = listEvent->next;
|
|
sm_ContextPool.Delete(listEvent);
|
|
listEvent = nextEvent;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
audVehicleCollisionContext* audVehicleCollisionContextList::InitAndAddEventToList(CEntity* pOtherEntity, Vec3V_In velocityV, u32 typeFlags)
|
|
{
|
|
//bAlreadyInAudioList 1 in list, may not have body collision flag, if the new context has the flag then search for it
|
|
if(pOtherEntity && pOtherEntity->m_nFlags.bAlreadyInAudioList)
|
|
{
|
|
if(pOtherEntity->m_nFlags.bAlreadyInAudioList == 1 && (typeFlags & AUD_VEH_COLLISION_BODY))
|
|
{
|
|
audVehicleCollisionContext * listContext = m_List;
|
|
while(listContext)
|
|
{
|
|
if(listContext->otherEntity == pOtherEntity)
|
|
{
|
|
listContext->SetTypeFlag(AUD_VEH_COLLISION_BODY);
|
|
return NULL;
|
|
}
|
|
|
|
listContext = listContext->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//bAlreadyInAudioList 2 - Already in the list and no need to change flags so don't search or add
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//bAlreadyInAudioList 0 - not in list, needs to be added
|
|
|
|
// Try to use the reuse list first, if there is something there.
|
|
audVehicleCollisionContext* newContext = m_ReuseList;
|
|
if(newContext)
|
|
{
|
|
m_ReuseList = newContext->next;
|
|
newContext->next = NULL;
|
|
}
|
|
else if(naVerifyf(!sm_ContextPool.IsFull(), "Vehicle audio collision context pool is full"))
|
|
{
|
|
newContext = sm_ContextPool.New();
|
|
}
|
|
else
|
|
{
|
|
// Pool full.
|
|
return NULL;
|
|
}
|
|
|
|
newContext->Init(pOtherEntity, VEC3V_TO_VECTOR3(velocityV), typeFlags);
|
|
newContext->collisionEvent.Init();
|
|
|
|
// Link in with the list. Note that the Init() calls above clear the next
|
|
// pointer, so this has to be done after.
|
|
naAssert(!newContext->next);
|
|
newContext->next = m_List;
|
|
m_List = newContext;
|
|
|
|
if(pOtherEntity)
|
|
{
|
|
pOtherEntity->m_nFlags.bAlreadyInAudioList = (typeFlags & AUD_VEH_COLLISION_BODY) ? 2 : 1;
|
|
}
|
|
|
|
return newContext;
|
|
}
|
|
|
|
void audVehicleCollisionContext::Init(audVehicleCollisionContext & inEvent)
|
|
{
|
|
*this = inEvent;
|
|
}
|
|
|
|
audVehicleCollisionAudio::audVehicleCollisionAudio(audVehicleAudioEntity * parent)
|
|
{
|
|
m_Parent = parent;
|
|
m_ScrapeSound = NULL;
|
|
m_SlowScrapeSound = NULL;
|
|
m_GlassDebrisSound = NULL;
|
|
m_PartsDebrisSound = NULL;
|
|
m_ScrapeStopSoundMs = 0;
|
|
m_LastGlassDebrisTime = 0;
|
|
m_LastGlassDebrisVolLin = 0.f;
|
|
m_LastPartsDebrisTime = 0;
|
|
m_LastPartsDebrisVolLin = 0.f;
|
|
m_ImpactDamage = 0.f;
|
|
m_JumpLandMag = 0.f;
|
|
m_LastJumpLandTime = 0;
|
|
m_LastJumpLandScrapeTime = 0;
|
|
m_LastCarRollTime = 0;
|
|
m_LastCarRollVol = 0.0f;
|
|
m_CarRollSound = NULL;
|
|
m_VehicleCollisionSettings = NULL;
|
|
m_CollisionMaterialSettings = NULL;
|
|
m_FoliageLoop = NULL;
|
|
m_TrainLoop = NULL;
|
|
m_WreckedTime = 0;
|
|
m_JumpStartTime = 0;
|
|
m_PlayingFoliageThisFrame = false;
|
|
m_ImpactThisFrame = false;
|
|
m_WasHitByTrain = false;
|
|
m_IsOnAnotherVehicle = false;
|
|
m_LastTimeHitByTrain = 0;
|
|
m_LastTrailerBumpTime = 0;
|
|
m_LastFakeImpactTime = 0;
|
|
m_LastBodyCollisionTime = 0;
|
|
m_LastFakeImpactVol = -100.f;
|
|
m_LastVehicleCollisionVolume = -100.f;
|
|
m_LastVehicleCollisionTime = 0;
|
|
m_LastMeleeTime = 0;
|
|
m_FakeImpactMagThisFrame = 0.f;
|
|
m_NeedsToPlayVehOnVehImpact = false;
|
|
}
|
|
|
|
void audVehicleCollisionAudio::InitClass()
|
|
{
|
|
static u32 curveName = ATSTRINGHASH("VEH_LAND_FACTOR", 0x991bcad6);
|
|
sm_VehicleLandFactorCurve.Init(curveName);
|
|
static u32 rolloffCurveName = ATSTRINGHASH("VEH_COLLISION_ROLLOFF_BOOST", 0xB9455E41);
|
|
sm_VehicleCollisionRolloffBoostCurve.Init(rolloffCurveName);
|
|
StaticConditionalWarning(sm_SoundSet.Init(ATSTRINGHASH("VEH_COLLISIONS_SET", 0xAB3A5029)), "Failed to find VEH_COLLISIONS_SET sound set");
|
|
|
|
}
|
|
|
|
void audVehicleCollisionAudio::UpdateSound(audSound *UNUSED_PARAM(sound), audRequestedSettings *reqSets, u32 UNUSED_PARAM(timeInMs))
|
|
{
|
|
u32 clientVariable;
|
|
reqSets->GetClientVariable(clientVariable);
|
|
audVehicleSounds soundId = (audVehicleSounds)clientVariable;
|
|
|
|
u32 now = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
switch(soundId)
|
|
{
|
|
case AUD_VEHICLE_SOUND_SCRAPE:
|
|
|
|
if(m_ScrapeSound)
|
|
{
|
|
m_ScrapeSound->SetRequestedPosition(VEC3V_TO_VECTOR3(GetVehicle()->GetTransform().GetPosition()));//m_Vehicle->TransformIntoWorldSpace(m_ClatterOffsetPos));
|
|
}
|
|
if(m_SlowScrapeSound)
|
|
{
|
|
m_SlowScrapeSound->SetRequestedPosition(VEC3V_TO_VECTOR3(GetVehicle()->GetTransform().GetPosition()));
|
|
}
|
|
if(now > m_ScrapeStopSoundMs)
|
|
{
|
|
if(m_ScrapeSound)
|
|
{
|
|
m_ScrapeSound->StopAndForget();
|
|
m_ScrapeSound = NULL;
|
|
}
|
|
if(m_SlowScrapeSound)
|
|
{
|
|
m_SlowScrapeSound->StopAndForget();
|
|
m_SlowScrapeSound = NULL;
|
|
}
|
|
}
|
|
break;
|
|
case AUD_VEHICLE_SOUND_FOLIAGE:
|
|
if(!m_PlayingFoliageThisFrame && m_FoliageLoop)
|
|
{
|
|
m_FoliageLoop->StopAndForget();
|
|
}
|
|
m_PlayingFoliageThisFrame = false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void audVehicleCollisionAudio::TriggerDebrisSounds(const f32 impactVelocity)
|
|
{
|
|
if(GetParent()->GetDrowningFactor() >= 1.f && !Water::IsCameraUnderwater())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const u32 timeInMs = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
VehicleCollisionSettings * settings = GetVehicleCollisionSettings();
|
|
|
|
f32 speedVol = audCurveRepository::GetLinearInterpolatedValue(0.0f, 1.0f, settings->CollisionMin, settings->CollisionMax, impactVelocity);
|
|
f32 angVol = audCurveRepository::GetLinearInterpolatedValue(0.0f, 1.0f, g_MinCarRollSpeed, g_MaxCarRollSpeed, GetVehicle()->GetAngVelocity().Mag());
|
|
|
|
audSoundInitParams initParams;
|
|
initParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
initParams.Tracker = GetVehicle()->GetPlaceableTracker();
|
|
const f32 volLin = Max(angVol, speedVol);
|
|
const f32 dbVol = audDriverUtil::ComputeDbVolumeFromLinear(volLin);
|
|
|
|
if(!m_PartsDebrisSound)
|
|
{
|
|
if(timeInMs > m_LastPartsDebrisTime + 250)
|
|
{
|
|
f32 bodyDamageFactor = Max(m_Parent->GetScriptBodyDamageFactor(), 1.0f - (GetVehicle()->GetVehicleDamage()->GetBodyHealth() / CVehicleDamage::GetBodyHealthMax()));
|
|
|
|
if(bodyDamageFactor > g_BodyDamageFactorForDebris)
|
|
{
|
|
GetParent()->CreateSound_PersistentReference(settings->PostImpactDebris, &m_PartsDebrisSound, &initParams);
|
|
m_LastPartsDebrisTime = timeInMs;
|
|
m_LastPartsDebrisVolLin = volLin;
|
|
if(m_PartsDebrisSound)
|
|
{
|
|
m_PartsDebrisSound->SetRequestedVolume(dbVol);
|
|
m_PartsDebrisSound->PrepareAndPlay();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(volLin > m_LastPartsDebrisVolLin)
|
|
{
|
|
m_LastPartsDebrisVolLin = volLin;
|
|
m_PartsDebrisSound->SetRequestedVolume(audDriverUtil::ComputeDbVolumeFromLinear(dbVol));
|
|
}
|
|
|
|
if(!m_GlassDebrisSound)
|
|
{
|
|
if(GetParent()->HasWindowBeenSmashed())
|
|
{
|
|
if(timeInMs > m_LastGlassDebrisVolLin + 250)
|
|
{
|
|
GetParent()->CreateSound_PersistentReference(settings->GlassDebris, &m_GlassDebrisSound, &initParams);
|
|
m_LastGlassDebrisTime = timeInMs;
|
|
m_LastGlassDebrisVolLin = volLin;
|
|
if(m_GlassDebrisSound)
|
|
{
|
|
m_GlassDebrisSound->SetRequestedVolume(dbVol);
|
|
m_GlassDebrisSound->PrepareAndPlay();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(volLin > m_LastGlassDebrisVolLin)
|
|
{
|
|
m_LastGlassDebrisVolLin = volLin;
|
|
m_GlassDebrisSound->SetRequestedVolume(audDriverUtil::ComputeDbVolumeFromLinear(dbVol));
|
|
}
|
|
}
|
|
|
|
CVehicle * audVehicleCollisionAudio::GetVehicle()
|
|
{
|
|
return GetParent()->GetVehicle();
|
|
}
|
|
|
|
void audVehicleCollisionAudio::TriggerUpsideDownRoll(const f32 impulseMag)
|
|
{
|
|
const u32 timeInMs = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
// turn the impulse and speed into a volume - don't retrigger too often, but up the volume if the impulse,speed increase
|
|
if(impulseMag > g_MinCarRollImpulseMag && GetVehicle()->GetVelocity().Mag() > g_MinCarRollSpeed)
|
|
{
|
|
if(!m_CarRollSound)
|
|
{
|
|
if(timeInMs > m_LastCarRollTime + 250)
|
|
{
|
|
// f32 angularVelocity = m_Vehicle->GetTurnSpeed().Mag();
|
|
audSoundInitParams initParams;
|
|
initParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
initParams.Tracker = GetVehicle()->GetPlaceableTracker();
|
|
|
|
if(GetVehicleCollisionSettings())
|
|
{
|
|
GetParent()->CreateSound_PersistentReference(GetVehicleCollisionSettings()->RollSound, &m_CarRollSound, &initParams);
|
|
}
|
|
|
|
m_LastCarRollVol = 0.f;
|
|
m_LastCarRollTime = timeInMs;
|
|
if(m_CarRollSound)
|
|
{
|
|
m_CarRollSound->SetRequestedVolume(0.f);
|
|
m_CarRollSound->PrepareAndPlay();
|
|
}
|
|
// Warningf("Roll: imp: %.2f; sp: %.2f; av: %.2f", impulseMag, m_Vehicle->GetVelocity().Mag(), angularVelocity);
|
|
}
|
|
}
|
|
if(m_CarRollSound)
|
|
{
|
|
f32 impulseVol = audCurveRepository::GetLinearInterpolatedValue(0.0f, 1.0f, g_MinCarRollImpulseMag, g_MaxCarRollImpulseMag, impulseMag);
|
|
f32 speedVol = audCurveRepository::GetLinearInterpolatedValue(0.0f, 1.0f, g_MinCarRollSpeed, g_MaxCarRollSpeed, GetVehicle()->GetVelocity().Mag());
|
|
const f32 vol = impulseVol * speedVol;
|
|
if(vol>m_LastCarRollVol)
|
|
{
|
|
m_LastCarRollVol = vol;
|
|
m_CarRollSound->SetRequestedVolume(audDriverUtil::ComputeDbVolumeFromLinear(vol));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void audVehicleCollisionAudio::ProcessImpactBoat(phContactIterator& impacts)
|
|
{
|
|
// Cache some static values out
|
|
CVehicle * pVehicle = GetVehicle();
|
|
const Vec3V velocityV = VECTOR3_TO_VEC3V(pVehicle->GetVelocity());
|
|
// const Vec3V transformC = pVehicle->GetTransform().GetC();
|
|
const Mat34V& mtrx = pVehicle->GetMatrixRef();
|
|
|
|
const Vec3V transformAV = mtrx.GetCol0();
|
|
const Vec3V transformBV = mtrx.GetCol1();
|
|
const Vec3V transformCV = mtrx.GetCol2();
|
|
|
|
const phContact & contact = impacts.GetContact();
|
|
const Vec3V contactWorldNormal = contact.GetWorldNormal();
|
|
|
|
Vec3V myNormalV;
|
|
impacts.GetMyNormal(myNormalV);
|
|
|
|
// Take these vectors
|
|
// myNormalV
|
|
// contactWorldNormal
|
|
// contactWorldNormal
|
|
// velocityV
|
|
// and transpose them so that all their X components are in one Vec4V, all the Y
|
|
// components in another, etc.
|
|
const Vec4V temp0BV = MergeXY(Vec4V(myNormalV), Vec4V(contactWorldNormal));
|
|
const Vec4V temp1BV = MergeXY(Vec4V(contactWorldNormal), Vec4V(velocityV));
|
|
const Vec4V temp2BV = MergeZW(Vec4V(myNormalV), Vec4V(contactWorldNormal));
|
|
const Vec4V temp3BV = MergeZW(Vec4V(contactWorldNormal), Vec4V(velocityV));
|
|
const Vec4V normalMyContactContactVelXV = MergeXY(temp0BV, temp1BV);
|
|
const Vec4V normalMyContactContactVelYV = MergeZW(temp0BV, temp1BV);
|
|
const Vec4V normalMyContactContactVelZV = MergeXY(temp2BV, temp3BV);
|
|
|
|
// Take these vectors
|
|
// transformAV
|
|
// transformBV
|
|
// transformCV
|
|
// transformCV
|
|
// and transpose them so that all their X components are in one Vec4V, all the Y
|
|
// components in another, etc.
|
|
const Vec4V temp0AV = MergeXY(Vec4V(transformAV), Vec4V(transformCV));
|
|
const Vec4V temp1AV = MergeXY(Vec4V(transformBV), Vec4V(transformCV));
|
|
const Vec4V temp2AV = MergeZW(Vec4V(transformAV), Vec4V(transformCV));
|
|
const Vec4V temp3AV = MergeZW(Vec4V(transformBV), Vec4V(transformCV));
|
|
const Vec4V transformABCCXV = MergeXY(temp0AV, temp1AV);
|
|
const Vec4V transformABCCYV = MergeZW(temp0AV, temp1AV);
|
|
const Vec4V transformABCCZV = MergeXY(temp2AV, temp3AV);
|
|
|
|
// Compute four dot products:
|
|
// myNormalV*transformAV
|
|
// contactWorldNormal*transformBV
|
|
// contactWorldNormal*transformCV
|
|
// velocityV*transformCV
|
|
const Vec4V dotXV = Scale(transformABCCXV, normalMyContactContactVelXV);
|
|
const Vec4V dotXYV = AddScaled(dotXV, transformABCCYV, normalMyContactContactVelYV);
|
|
const Vec4V dotV = AddScaled(dotXYV, transformABCCZV, normalMyContactContactVelZV);
|
|
|
|
// We need the absolute values of some of the dot products.
|
|
const Vec4V absDotV = Abs(dotV);
|
|
|
|
u32 impactType = AUD_VEH_COLLISION_BODY;
|
|
|
|
const ScalarV contactDotThresholdV = LoadScalar32IntoScalarV(g_BoatBottomContactDotThreshold);
|
|
|
|
|
|
const ScalarV absContactDotCV = absDotV.GetZ();
|
|
if(IsGreaterThanAll(absContactDotCV, contactDotThresholdV))
|
|
{
|
|
impactType |= AUD_VEH_COLLISION_BOTTOM;
|
|
}
|
|
|
|
CEntity* pOtherEntity = CPhysics::GetEntityFromInst(impacts.GetOtherInstance());
|
|
|
|
audVehicleCollisionContext* pImpactContext = m_CollisionEventsList.InitAndAddEventToList(pOtherEntity, velocityV, impactType);
|
|
if(pImpactContext)
|
|
{
|
|
#if !__FINAL
|
|
pImpactContext->parent = pVehicle;
|
|
#endif
|
|
}
|
|
|
|
|
|
#if !__FINAL
|
|
if(sm_ContextPool.GetFreeCount() < 20)
|
|
{
|
|
naDisplayf("Vehicle collision context pool getting full, only %d slots left, frame %d time %d", sm_ContextPool.GetFreeCount(), fwTimer::GetFrameCount(), audNorthAudioEngine::GetCurrentTimeInMs());
|
|
}
|
|
|
|
if(sm_ContextPool.IsFull() && !sm_debugEventListFull) //Extra debug info to try and hunt down a rare assert
|
|
{
|
|
sm_debugEventListFull = true;
|
|
sm_debugEventCounter = 100;
|
|
naDisplayf("Context pool is full, frame: %d, time %d", fwTimer::GetFrameCount(), audNorthAudioEngine::GetCurrentTimeInMs());
|
|
for(int i=0; i < audVehicleCollisionContextList::k_MaxCollisionEvents; i++)
|
|
{
|
|
audVehicleCollisionContext & context = sm_ContextPool.GetElement(i);
|
|
naDisplayf("Index %d, entity %p, model %s, other entity %p, other model %s", i, context.parent.Get(), context.parent->GetModelName(), context.otherEntity.Get(), context.otherEntity.Get() ? context.otherEntity->GetModelName() : "NONE");
|
|
}
|
|
}
|
|
else if(!sm_ContextPool.IsFull() && sm_debugEventCounter > 0)
|
|
{
|
|
naDisplayf("Context pool is NOT full: free count (%d)/(%d), frame: %d, entity %p, model %s, other entity %p, other model %s", sm_ContextPool.GetFreeCount(), audVehicleCollisionContextList::k_MaxCollisionEvents, fwTimer::GetFrameCount(), pVehicle, pVehicle->GetModelName(), pOtherEntity, pOtherEntity ? pOtherEntity->GetModelName() : "NONE");
|
|
sm_debugEventCounter--;
|
|
sm_debugEventListFull = false;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void audVehicleCollisionAudio::ProcessFoliageImpact(f32 drag)
|
|
{
|
|
if(!GetVehicle()->GetDriver() || GetVehicle()->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_IsEnteringOrExitingVehicle))
|
|
{
|
|
return;
|
|
}
|
|
m_Foliage.numCollisions++;
|
|
m_Foliage.drag += drag;
|
|
}
|
|
|
|
|
|
bool audVehicleCollisionAudio::StartProcessImpacts()
|
|
{
|
|
// Get the current frame from the event list early to avoid LHS stall
|
|
u32 frameCount = fwTimer::GetFrameCount();
|
|
u32 listFrameCount = m_CollisionEventsList.GetFrameCount();
|
|
|
|
// adding the heli check allows burnt out heli bodies to make ground impact sounds
|
|
if(GetParent()->IsDisabled())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(frameCount > listFrameCount)
|
|
{
|
|
m_CollisionEventsList.ResetToReuseList();
|
|
m_CollisionEventsList.SetFrameCount(frameCount);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void audVehicleCollisionAudio::EndProcessImpacts()
|
|
{
|
|
m_CollisionEventsList.ClearReuseList();
|
|
}
|
|
|
|
|
|
// Defined in 'Vehicles/wheel.cpp':
|
|
extern Vec4V g_WheelImpactNormalSideLimitsV;
|
|
|
|
void audVehicleCollisionAudio::ProcessImpact(phCachedContactConstIterator& impacts)
|
|
{
|
|
// Cache some static values out
|
|
CVehicle * pVehicle = GetVehicle();
|
|
const Vec3V velocityV = VECTOR3_TO_VEC3V(pVehicle->GetVelocity());
|
|
const Mat34V& mtrx = pVehicle->GetMatrixRef();
|
|
|
|
const Vec3V transformAV = mtrx.GetCol0();
|
|
const Vec3V transformBV = mtrx.GetCol1();
|
|
const Vec3V transformCV = mtrx.GetCol2();
|
|
const int numWheels = pVehicle->GetNumWheels();
|
|
const CWheel * const * ppWheels = pVehicle->GetWheels();
|
|
|
|
CEntity* pOtherEntity = CPhysics::GetEntityFromInst(impacts.GetOtherInstance());
|
|
|
|
const phContact & contact = impacts.GetContact();
|
|
const Vec3V contactWorldNormal = contact.GetWorldNormal();
|
|
|
|
Vec3V myNormalV;
|
|
impacts.GetMyNormal(myNormalV);
|
|
|
|
// Take these vectors
|
|
// myNormalV
|
|
// contactWorldNormal
|
|
// contactWorldNormal
|
|
// velocityV
|
|
// and transpose them so that all their X components are in one Vec4V, all the Y
|
|
// components in another, etc.
|
|
const Vec4V temp0BV = MergeXY(Vec4V(myNormalV), Vec4V(contactWorldNormal));
|
|
const Vec4V temp1BV = MergeXY(Vec4V(contactWorldNormal), Vec4V(velocityV));
|
|
const Vec4V temp2BV = MergeZW(Vec4V(myNormalV), Vec4V(contactWorldNormal));
|
|
const Vec4V temp3BV = MergeZW(Vec4V(contactWorldNormal), Vec4V(velocityV));
|
|
const Vec4V normalMyContactContactVelXV = MergeXY(temp0BV, temp1BV);
|
|
const Vec4V normalMyContactContactVelYV = MergeZW(temp0BV, temp1BV);
|
|
const Vec4V normalMyContactContactVelZV = MergeXY(temp2BV, temp3BV);
|
|
|
|
// Take these vectors
|
|
// transformAV
|
|
// transformBV
|
|
// transformCV
|
|
// transformCV
|
|
// and transpose them so that all their X components are in one Vec4V, all the Y
|
|
// components in another, etc.
|
|
const Vec4V temp0AV = MergeXY(Vec4V(transformAV), Vec4V(transformCV));
|
|
const Vec4V temp1AV = MergeXY(Vec4V(transformBV), Vec4V(transformCV));
|
|
const Vec4V temp2AV = MergeZW(Vec4V(transformAV), Vec4V(transformCV));
|
|
const Vec4V temp3AV = MergeZW(Vec4V(transformBV), Vec4V(transformCV));
|
|
const Vec4V transformABCCXV = MergeXY(temp0AV, temp1AV);
|
|
const Vec4V transformABCCYV = MergeZW(temp0AV, temp1AV);
|
|
const Vec4V transformABCCZV = MergeXY(temp2AV, temp3AV);
|
|
|
|
// Compute four dot products:
|
|
// myNormalV*transformAV
|
|
// contactWorldNormal*transformBV
|
|
// contactWorldNormal*transformCV
|
|
// velocityV*transformCV
|
|
const Vec4V dotXV = Scale(transformABCCXV, normalMyContactContactVelXV);
|
|
const Vec4V dotXYV = AddScaled(dotXV, transformABCCYV, normalMyContactContactVelYV);
|
|
const Vec4V dotV = AddScaled(dotXYV, transformABCCZV, normalMyContactContactVelZV);
|
|
|
|
// We need the absolute values of some of the dot products.
|
|
const Vec4V absDotV = Abs(dotV);
|
|
|
|
|
|
u32 impactType = AUD_VEH_COLLISION_BODY;
|
|
|
|
const ScalarV contactDotThresholdV(0.6f);
|
|
|
|
const ScalarV absContactDotCV = absDotV.GetZ();
|
|
if(IsGreaterThanAll(absContactDotCV, contactDotThresholdV))
|
|
{
|
|
impactType |= AUD_VEH_COLLISION_BOTTOM;
|
|
}
|
|
else
|
|
{
|
|
const ScalarV absContactDotBV = absDotV.GetY();
|
|
if(IsGreaterThanAll(absContactDotBV, contactDotThresholdV))
|
|
{
|
|
m_ImpactThisFrame = true;
|
|
|
|
if(pOtherEntity && pOtherEntity->GetIsTypePed())
|
|
{
|
|
((CPed*)pOtherEntity)->GetPedAudioEntity()->NotifyVehicleContact(GetVehicle());
|
|
}
|
|
|
|
if(GetParent()->GetAudioVehicleType() == AUD_VEHICLE_TRAIN && ((CTrain*)pVehicle)->IsEngine() && pOtherEntity && pOtherEntity->GetIsTypeVehicle())
|
|
{
|
|
audVehicleCollisionAudio& otherAudio = ((CVehicle*)(pOtherEntity))->GetVehicleAudioEntity()->GetCollisionAudio();
|
|
if(!otherAudio.m_WasHitByTrain && ! otherAudio.m_IsOnAnotherVehicle)
|
|
{
|
|
// What's the purpose of this stuff? Normalizing the vectors,
|
|
// computing a dot product, and checking if it's less than exactly one
|
|
// doesn't seem to make sense - seems like that just always
|
|
// passes except for if they happen to be exactly aligned,
|
|
// except that it will be pretty sensitive to numerical errors, in particular
|
|
// since we use NormalizeFast(). /FF
|
|
|
|
Vec3V thisNormal = NormalizeFast(velocityV);
|
|
Vec3V otherNormal = NormalizeFast(VECTOR3_TO_VEC3V(otherAudio.GetVehicle()->GetVelocity()));
|
|
if(Dot(thisNormal, otherNormal).Getf() < 1.f)
|
|
{
|
|
otherAudio.m_WasHitByTrain = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Process wheel audio
|
|
if(m_Parent->GetVehicleModelNameHash() != ATSTRINGHASH("DELUXO", 0x586765FB) || m_Parent->GetVehicle()->GetSpecialFlightModeRatio() == 0.f)
|
|
{
|
|
const int myComponent = impacts.GetMyComponent();
|
|
for(int i=0; i < numWheels; i++)
|
|
{
|
|
const CWheel * wheel = ppWheels[i];
|
|
if(myComponent == wheel->GetFragChild())
|
|
{
|
|
impactType &= ~AUD_VEH_COLLISION_BODY;
|
|
|
|
// Adapted from the old CWheel::ImpactIsOnSide():
|
|
|
|
int nOtherInstType = PH_INST_GTA;
|
|
phInst* pOtherInst = impacts.GetOtherInstance();
|
|
if(pOtherInst)
|
|
{
|
|
nOtherInstType = pOtherInst->GetClassType();
|
|
}
|
|
|
|
const Vec4V wheelImpactNormalSideLimitsV = g_WheelImpactNormalSideLimitsV;
|
|
|
|
ScalarV sideNormalLimitV;
|
|
if(wheel->GetConfigFlags().IsFlagSet(WCF_BIKE_FALLEN_COLLIDER))
|
|
{
|
|
sideNormalLimitV = wheelImpactNormalSideLimitsV.GetZ();
|
|
}
|
|
else if(nOtherInstType==PH_INST_VEH || nOtherInstType==PH_INST_FRAG_VEH)
|
|
{
|
|
sideNormalLimitV = wheelImpactNormalSideLimitsV.GetY();
|
|
}
|
|
else
|
|
{
|
|
sideNormalLimitV = wheelImpactNormalSideLimitsV.GetX();
|
|
}
|
|
|
|
const ScalarV sideNormalV = absDotV.GetX();
|
|
if(IsLessThanOrEqualAll(sideNormalV, sideNormalLimitV))
|
|
{
|
|
|
|
if(impactType & AUD_VEH_COLLISION_BOTTOM)
|
|
{
|
|
const ScalarV absDotVelCV = absDotV.GetW();
|
|
|
|
impactType |= AUD_VEH_COLLISION_WHEEL_BOTTOM;
|
|
|
|
const ScalarV jumpLandMagOldV = LoadScalar32IntoScalarV(m_JumpLandMag);
|
|
const ScalarV jumpLandMagNewV = Max(jumpLandMagOldV, absDotVelCV);
|
|
StoreScalar32FromScalarV(m_JumpLandMag, jumpLandMagNewV);
|
|
|
|
if(pOtherEntity && pOtherEntity->GetIsTypeVehicle())
|
|
{
|
|
audVehicleCollisionAudio& otherAudio = ((CVehicle*)(pOtherEntity))->GetVehicleAudioEntity()->GetCollisionAudio();
|
|
otherAudio.m_WasHitByTrain = false;
|
|
otherAudio.m_IsOnAnotherVehicle = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nOtherInstType==PH_INST_FRAG_VEH && !((CVehicleModelInfo*)GetVehicle()->GetBaseModelInfo())->GetIsTrailer() && !(pOtherEntity && pOtherEntity->GetIsTypeVehicle() && (((CVehicle*)pOtherEntity)->GetVehicleAudioEntity()->IsToyCar() || ((CVehicleModelInfo*)((CVehicle*)pOtherEntity)->GetBaseModelInfo())->GetIsTrailer())))
|
|
{
|
|
Fakeimpact(pVehicle->GetVelocity().Mag(), 1.f, true);
|
|
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
grcDebugDraw::Sphere(GetVehicle()->GetTransform().GetPosition() - GetVehicle()->GetTransform().GetA() + Vec3V(ScalarV(0.0f), ScalarV(0.f), ScalarV(1.5f)), 0.5f, Color32(255, 0, 255), true, 5);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
impactType |= AUD_VEH_COLLISION_WHEEL_SIDE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
audVehicleCollisionContext* pImpactContext = m_CollisionEventsList.InitAndAddEventToList(pOtherEntity, velocityV, impactType);
|
|
if(pImpactContext)
|
|
{
|
|
audVehicleCollisionContext& impactContext = *pImpactContext;
|
|
|
|
#if !__FINAL
|
|
impactContext.parent = pVehicle;
|
|
#endif
|
|
//Ped manifold collisions don't trigger on bikes so we have to fake it
|
|
if((pVehicle->GetVehicleType() == VEHICLE_TYPE_BIKE || pVehicle->GetVehicleType() == VEHICLE_TYPE_BICYCLE )
|
|
&& pOtherEntity && pOtherEntity->GetIsTypePed() && pVehicle->GetDriver() && pOtherEntity != pVehicle->GetDriver())
|
|
{
|
|
impactContext.canPlayCollision = true;
|
|
impactContext.impactVelocity = Mag(velocityV).Getf();
|
|
if(!FPIsFinite(impactContext.impactVelocity))
|
|
{
|
|
impactContext.impactVelocity = impactContext.impactMag;
|
|
if(!FPIsFinite(impactContext.impactVelocity))
|
|
{
|
|
impactContext.impactVelocity = 0.f;
|
|
}
|
|
}
|
|
impactContext.impactMag = impactContext.impactVelocity;
|
|
impactContext.impactVolume = 0.f;
|
|
impactContext.collisionEvent.component = (u16)impacts.GetMyComponent();
|
|
impactContext.collisionEvent.impactMag = impactContext.impactMag;
|
|
impactContext.collisionEvent.normal = contactWorldNormal;
|
|
impactContext.collisionEvent.otherComponent = (u16)impacts.GetOtherComponent();
|
|
impactContext.collisionEvent.pos = impacts.GetMyPosition();
|
|
impactContext.collisionEvent.otherPos = impacts.GetOtherPosition();
|
|
impactContext.collisionEvent.otherMaterialId = impacts.GetOtherMaterialId();
|
|
}
|
|
}
|
|
|
|
|
|
#if !__FINAL
|
|
if(sm_ContextPool.GetFreeCount() < 20)
|
|
{
|
|
naDisplayf("Vehicle collision context pool getting full, only %d slots left, frame %d time %d", sm_ContextPool.GetFreeCount(), fwTimer::GetFrameCount(), audNorthAudioEngine::GetCurrentTimeInMs());
|
|
}
|
|
|
|
if(sm_ContextPool.IsFull() && !sm_debugEventListFull) //Extra debug info to try and hunt down a rare assert
|
|
{
|
|
sm_debugEventListFull = true;
|
|
sm_debugEventCounter = 100;
|
|
naDisplayf("Context pool is full, frame: %d, time %d", fwTimer::GetFrameCount(), audNorthAudioEngine::GetCurrentTimeInMs());
|
|
for(int i=0; i < audVehicleCollisionContextList::k_MaxCollisionEvents; i++)
|
|
{
|
|
audVehicleCollisionContext & context = sm_ContextPool.GetElement(i);
|
|
naDisplayf("Index %d, entity %p, model %s, other entity %p, other model %s", i, context.parent.Get(), context.parent->GetModelName(), context.otherEntity.Get(), context.otherEntity.Get() ? context.otherEntity->GetModelName() : "NONE");
|
|
}
|
|
}
|
|
else if(!sm_ContextPool.IsFull() && sm_debugEventCounter > 0)
|
|
{
|
|
naDisplayf("Context pool is NOT full: free count (%d)/(%d), frame: %d, entity %p, model %s, other entity %p, other model %s", sm_ContextPool.GetFreeCount(), audVehicleCollisionContextList::k_MaxCollisionEvents, fwTimer::GetFrameCount(), pVehicle, pVehicle->GetModelName(), pOtherEntity, pOtherEntity ? pOtherEntity->GetModelName() : "NONE");
|
|
sm_debugEventCounter--;
|
|
sm_debugEventListFull = false;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void audVehicleCollisionAudio::ProcessContact(VfxCollisionInfo_s &UNUSED_PARAM(collisionInfo), phManifold & manifold, CEntity * otherEntity, const audVehicleCollisionEvent & collisionEvent)
|
|
{
|
|
audVehicleCollisionContext * playContext = m_CollisionEventsList.GetEvents();
|
|
|
|
//#if __BANK
|
|
// if(g_DebugVehicleCollisionAudio)
|
|
// {
|
|
// grcDebugDraw::Sphere(GetVehicle()->GetTransform().GetPosition() - GetVehicle()->GetTransform().GetA() + Vec3V(ScalarV(0.0f), ScalarV(0.f), ScalarV(1.5f)), 0.5f, Color32(255, 0, 255), true, 5);
|
|
// }
|
|
//#endif
|
|
|
|
|
|
if(GetVehicle()->GetVehicleType() == VEHICLE_TYPE_BICYCLE && g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) < (m_LastMeleeTime + sm_TimeForBikePostMeleeImpacts))
|
|
{
|
|
Fakeimpact(collisionEvent.impactMag, 1.f, true);
|
|
}
|
|
|
|
while(playContext)
|
|
{
|
|
if (playContext->otherEntity == otherEntity)
|
|
{
|
|
if(IsBike() && m_Parent->GetVehicle()->GetSpecialFlightModeRatio() == 0.0f && playContext->GetTypeFlag(AUD_VEH_COLLISION_BOTTOM))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if(IsTrailer())
|
|
{
|
|
if(playContext->otherEntity && playContext->otherEntity->GetIsTypeVehicle())
|
|
{
|
|
CVehicle *vehicle = (CVehicle*)(playContext->otherEntity.Get());
|
|
if(vehicle->GetAttachedTrailer() == GetVehicle())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
f32 scrapeMag = collisionEvent.scrapeMag * g_CollisionAudioEntity.GetAudioWeightScaling(g_CollisionAudioEntity.GetAudioWeight(otherEntity), AUDIO_WEIGHT_VH);
|
|
|
|
if(!FPIsFinite(scrapeMag))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if(otherEntity->GetIsTypePed())
|
|
{
|
|
scrapeMag = 0.f;
|
|
}
|
|
|
|
|
|
|
|
f32 vehicleSpeed = m_Parent->GetVehicle()->GetVelocity().Mag();
|
|
|
|
if(otherEntity->GetIsTypeVehicle())
|
|
{
|
|
if(vehicleSpeed < sm_MinSpeedForVehicleCollisions)
|
|
{
|
|
CVehicle * otherVehicle = (CVehicle*)otherEntity;
|
|
if(otherVehicle->GetVelocity().Mag() < sm_MinSpeedForVehicleCollisions)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
playContext->SetTypeFlag(AUD_VEH_COLLISION_ON_VEHICLE);
|
|
}
|
|
|
|
if(IsBike() && (!otherEntity || otherEntity->GetIsTypeBuilding() || (!GetVehicle()->GetDriver() && otherEntity->GetIsTypePed())))
|
|
{
|
|
if(vehicleSpeed < collisionEvent.impactMag || (otherEntity->GetIsTypePed() && !((CPed*)otherEntity)->GetUsingRagdoll()))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(otherEntity->GetIsTypeBuilding() &&
|
|
(GetVehicle()->GetVehicleType() == VEHICLE_TYPE_CAR &&
|
|
(GetVehicle()->IsUpsideDown())))
|
|
{
|
|
playContext->SetTypeFlag(AUD_VEH_COLLISION_UPSIDEDOWN);
|
|
GetParent()->SetIsOnGround();
|
|
}
|
|
u32 doorIndex = ~0U;
|
|
if(otherEntity->GetType() == ENTITY_TYPE_OBJECT && !otherEntity->IsBaseFlagSet(fwEntity::IS_FIXED) &&
|
|
((CObject*)otherEntity)->IsADoor() &&
|
|
(((CDoor*)otherEntity)->IsCreakingDoorType()))
|
|
{
|
|
doorIndex = 0;
|
|
}
|
|
|
|
if(doorIndex != ~0U)
|
|
{
|
|
// we have a ped/door collision:
|
|
// these are now handled in the dooraudioentity
|
|
}
|
|
|
|
|
|
if((playContext->GetTypeFlag(AUD_VEH_COLLISION_BODY) || playContext->GetTypeFlag(AUD_VEH_COLLISION_WHEEL_SIDE)
|
|
|| (playContext->otherEntity && playContext->otherEntity->GetIsTypePed()) )
|
|
&& (!g_CollisionAudioEntity.IsScrapeFromMagnitudes(collisionEvent.impactMag, scrapeMag) ||
|
|
vehicleSpeed > g_ScrapeImpactVel))
|
|
{
|
|
bool isOkayToPlayImpacts = g_CollisionAudioEntity.IsOkToPlayImpacts(manifold);
|
|
isOkayToPlayImpacts &= IsOkToPlayImpacts(playContext);
|
|
|
|
if(isOkayToPlayImpacts)
|
|
{
|
|
playContext->canPlayCollision = true;
|
|
}
|
|
if(playContext->canPlayCollision && collisionEvent.impactMag > playContext->impactMag)
|
|
{
|
|
playContext->collisionEvent.Init(collisionEvent);
|
|
playContext->impactMag = collisionEvent.impactMag;
|
|
playContext->impactVelocity = vehicleSpeed;
|
|
if(!FPIsFinite(playContext->impactVelocity))
|
|
{
|
|
playContext->impactVelocity = 0.f;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(g_CollisionAudioEntity.IsScrapeFromMagnitudes(collisionEvent.impactMag, scrapeMag))
|
|
{
|
|
ProcessScrape(playContext, manifold, collisionEvent);
|
|
}
|
|
break;
|
|
}
|
|
|
|
playContext = playContext->next;
|
|
}
|
|
}
|
|
|
|
bool audVehicleCollisionAudio::IsOkToPlayImpacts(audVehicleCollisionContext* playContext)
|
|
{
|
|
if (playContext && m_Parent && m_Parent->GetVehicle()->InheritsFromBoat())
|
|
{
|
|
// Prevent boats from spamming body collisions
|
|
if (playContext->GetTypeFlag(AUD_VEH_COLLISION_BODY))
|
|
{
|
|
u32 lastCollision = m_LastBodyCollisionTime;
|
|
u32 currentTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
m_LastBodyCollisionTime = currentTime;
|
|
|
|
u32 timeDelta = currentTime - lastCollision;
|
|
|
|
// url:bugstar:6760524 - Boat Audio - VEH_DYNAMIC_IMPACTS sounds are spamming when you beach boats
|
|
if (timeDelta < g_MinTimeBetweenCollisions && (m_Parent->GetVehicle()->GetModelNameHash() == ATSTRINGHASH("PATROLBOAT", 0xEF813606) || g_ApplyBoatBodyCollisionLimitingToAllBoats))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const f32 velocity = playContext->velocity.Mag2();
|
|
|
|
if (velocity < 0.25f)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
VehicleCollisionSettings * audVehicleCollisionAudio::GetVehicleCollisionSettings()
|
|
{
|
|
if(BANK_ONLY(!sm_UpdateVehicleCollisionSettings &&) m_VehicleCollisionSettings)
|
|
{
|
|
return m_VehicleCollisionSettings;
|
|
}
|
|
m_VehicleCollisionSettings = m_Parent->GetVehicleCollisionSettings();
|
|
|
|
if(naVerifyf(m_VehicleCollisionSettings, "Couldn't get vehicle collision settings for vehicle %s", GetParent()->GetVehicle()->GetModelName()))
|
|
{
|
|
m_SlowScrapeVolCurve.Init(m_VehicleCollisionSettings->SlowScrapeVolCurve);
|
|
m_ScrapeImpactCurve.Init(m_VehicleCollisionSettings->ScrapeImpactVolCurve);
|
|
m_SlowScrapeImpactCurve.Init(m_VehicleCollisionSettings->SlowScrapeImpactCurve);
|
|
m_ScrapePitchCurve.Init(m_VehicleCollisionSettings->ScrapePitchCurve);
|
|
m_ScrapeVolCurve.Init(m_VehicleCollisionSettings->ScrapeVolCurve);
|
|
}
|
|
else
|
|
{
|
|
m_VehicleCollisionSettings = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(ATSTRINGHASH("VEHICLE_COLLISION_CAR", 0xC2FB47));
|
|
}
|
|
|
|
return m_VehicleCollisionSettings;
|
|
}
|
|
|
|
void audVehicleCollisionAudio::ProcessScrape(audVehicleCollisionContext * scrapeContext, phManifold& manifold, const audVehicleCollisionEvent & scrapeEvent)
|
|
{
|
|
|
|
|
|
audCollisionEvent *collisionEvent = NULL;
|
|
bool hasFoundEvent = false;
|
|
|
|
collisionEvent = g_CollisionAudioEntity.GetCollisionEventFromHistory(manifold);
|
|
|
|
if(collisionEvent)
|
|
{
|
|
u16 eventIndex;
|
|
collisionEvent = g_CollisionAudioEntity.GetRollCollisionEventFromHistory(GetVehicle(), scrapeContext->otherEntity, &eventIndex);
|
|
if(collisionEvent)
|
|
{
|
|
g_CollisionAudioEntity.SetManifoldUserDataIndex(manifold, eventIndex);
|
|
}
|
|
}
|
|
|
|
//Do we have this event in the history?
|
|
if (collisionEvent)
|
|
{
|
|
const bool areEntities00Matched = (collisionEvent->entities[0] == scrapeContext->otherEntity && collisionEvent->components[0] == scrapeEvent.otherComponent);
|
|
const bool areEntities11Matched = (collisionEvent->entities[1] == GetVehicle() && collisionEvent->components[1] == scrapeEvent.component);
|
|
|
|
if(areEntities00Matched && areEntities11Matched)
|
|
{
|
|
//@@: location AUDVEHICLECOLLISIONAUDIO_PROCESSSCRAPE_FOUND_EVENT
|
|
hasFoundEvent = true;
|
|
}
|
|
}
|
|
|
|
if(!hasFoundEvent)
|
|
{
|
|
u16 eventIndex;
|
|
//We don't already have an event for collisions between these entities, so create a new one.
|
|
collisionEvent = g_CollisionAudioEntity.GetFreeCollisionEventFromHistory(&eventIndex);
|
|
|
|
if(collisionEvent == NULL)
|
|
{
|
|
naWarningf("The audio collision event history is full!");
|
|
return;
|
|
}
|
|
g_CollisionAudioEntity.SetManifoldUserDataIndex(manifold, eventIndex);
|
|
}
|
|
|
|
collisionEvent->entities[0] = scrapeContext->otherEntity;
|
|
collisionEvent->entities[1] = GetVehicle();
|
|
collisionEvent->positions[0] = VEC3V_TO_VECTOR3(scrapeEvent.otherPos);
|
|
collisionEvent->components[0] = scrapeEvent.otherComponent;
|
|
collisionEvent->components[1] = scrapeEvent.component;
|
|
collisionEvent->materialIds[0] = scrapeEvent.otherMaterialId;
|
|
collisionEvent->type = AUD_COLLISION_TYPE_SCRAPE;
|
|
|
|
collisionEvent->scrapeMagnitudes[0] = scrapeEvent.scrapeMag;
|
|
collisionEvent->scrapeMagnitudes[1] = 0.f;
|
|
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(scrapeEvent.otherMaterialId);
|
|
|
|
if(unpackedMtlIdA >= PGTAMATERIALMGR->GetNumMaterials())
|
|
{
|
|
return; //Invalid material id bail out
|
|
}
|
|
|
|
collisionEvent->materialSettings[0] = g_CollisionAudioEntity.GetMaterialOverride(scrapeContext->otherEntity, g_audCollisionMaterials[(s32)unpackedMtlIdA], scrapeEvent.otherComponent);
|
|
collisionEvent->materialSettings[1] = NULL;
|
|
|
|
if(scrapeContext->GetTypeFlag(AUD_VEH_COLLISION_BODY) && ((GetVehicle() && GetVehicle()->GetModelNameHash() == ATSTRINGHASH("Kosatka", 0x4FAF0D70)) || (collisionEvent->materialSettings[0] && collisionEvent->materialSettings[0]->HardImpact != g_NullSoundHash)))
|
|
{
|
|
scrapeContext->SetTypeFlag(AUD_VEH_COLLISION_SCRAPE);
|
|
if(scrapeEvent.scrapeMag > scrapeContext->scrapeMag)
|
|
{
|
|
scrapeContext->scrapeMag = scrapeEvent.scrapeMag;
|
|
}
|
|
if(!scrapeContext->otherEntity->GetIsTypeVehicle())
|
|
{
|
|
if(m_Parent->HydraulicsModifiedRecently())
|
|
{
|
|
if(!scrapeContext->otherEntity->GetIsTypeObject() && !scrapeContext->otherEntity->GetIsTypePed())
|
|
{
|
|
scrapeContext->scrapeMag = 0.f;
|
|
return;
|
|
}
|
|
}
|
|
|
|
scrapeContext->scrapeMag *= g_CollisionAudioEntity.GetAudioWeightScaling(AUDIO_WEIGHT_VH, g_CollisionAudioEntity.GetAudioWeight(scrapeContext->otherEntity));
|
|
}
|
|
}
|
|
|
|
if(scrapeContext->otherEntity->GetIsTypeVehicle())
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_CollisionAudioEntity.ProcessScrapeSounds(collisionEvent);
|
|
|
|
collisionEvent->scrapeStopTimeMs = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) + g_ScrapingCollisionHoldTimeMs;
|
|
|
|
}
|
|
|
|
void audVehicleCollisionAudio::ComputePitchAndVolumeFromScrapeSpeed(audCollisionEvent *collisionEvent, u32 index, s32 &pitch, f32 &volume)
|
|
{
|
|
pitch = 0;
|
|
volume = g_SilenceVolume;
|
|
|
|
CollisionMaterialSettings *materialSettings = collisionEvent->materialSettings[index];
|
|
CEntity * entity = collisionEvent->entities[index];
|
|
VehicleCollisionSettings * vehicleSettings = GetVehicleCollisionSettings();
|
|
|
|
if(materialSettings)
|
|
{
|
|
bool isMap = entity->GetCurrentPhysicsInst() && entity->GetCurrentPhysicsInst()->GetClassType() == PH_INST_MAPCOL;
|
|
|
|
if(entity->GetIsTypePed())
|
|
{
|
|
return;
|
|
}
|
|
|
|
f32 minScrapeSpeed = materialSettings->MinScrapeSpeed;
|
|
f32 maxScrapeSpeed = materialSettings->MaxScrapeSpeed;
|
|
|
|
if(!naVerifyf(minScrapeSpeed != maxScrapeSpeed, "MinsScrapeSpeed and MaxScrapeSpeed are the same for CollisionMaterialSettings %s", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings->NameTableOffset)))
|
|
{
|
|
maxScrapeSpeed += 0.1f;
|
|
}
|
|
|
|
if(isMap)
|
|
{
|
|
minScrapeSpeed = vehicleSettings->CollisionMin;
|
|
maxScrapeSpeed = vehicleSettings->CollisionMax;
|
|
|
|
if(!naVerifyf(minScrapeSpeed != maxScrapeSpeed, "MinsScrapeSpeed and MaxScrapeSpeed are the same for VehicleCollisionSettings %s", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(vehicleSettings->NameTableOffset)))
|
|
{
|
|
maxScrapeSpeed += 0.1f;
|
|
}
|
|
}
|
|
|
|
//Scale the scrape speed to a 0->1 range.
|
|
f32 scaledScrapeSpeed = ClampRange((collisionEvent->scrapeMagnitudes[index]- minScrapeSpeed) / (maxScrapeSpeed-minScrapeSpeed), 0.f, 1.0f);
|
|
|
|
if(!naVerifyf(FPIsFinite(scaledScrapeSpeed), "Got a NAN in ComputePitchAndVolumeFromScrapeSpeed"))
|
|
{
|
|
scaledScrapeSpeed = 0.f;
|
|
}
|
|
|
|
//Derive a pitch from a curve.
|
|
if(isMap)
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(m_ScrapePitchCurve.CalculateValue(scaledScrapeSpeed));
|
|
}
|
|
else if (AUD_GET_TRISTATE_VALUE(collisionEvent->materialSettings[index]->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(g_CollisionAudioEntity.GetScrapePitchCurve().CalculateValue(scaledScrapeSpeed));
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ScrapePitchCurve))
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(curve.CalculateValue(scaledScrapeSpeed));
|
|
}
|
|
}
|
|
|
|
//Derive a volume from a curve.
|
|
if(isMap)
|
|
{
|
|
f32 volumeLinear = m_ScrapeVolCurve.CalculateValue(scaledScrapeSpeed);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
else if (AUD_GET_TRISTATE_VALUE(collisionEvent->materialSettings[index]->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
f32 volumeLinear = g_CollisionAudioEntity.GetScrapeVolCurve().CalculateValue(scaledScrapeSpeed);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ScrapeVolCurve))
|
|
{
|
|
f32 volumeLinear = curve.CalculateValue(scaledScrapeSpeed);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pitch = 0;
|
|
volume = -100.f;
|
|
}
|
|
}
|
|
|
|
|
|
void audVehicleCollisionAudio::ProcessCollision(audVehicleCollisionContext * impactContext, const audVehicleCollisionEvent & collisionEvent)
|
|
{
|
|
VehicleCollisionSettings * settings = GetVehicleCollisionSettings();
|
|
|
|
CEntity * otherEntity = impactContext->otherEntity;
|
|
|
|
Vector3 posB = VEC3V_TO_VECTOR3(collisionEvent.otherPos);
|
|
|
|
AudioWeight weight, otherWeight;
|
|
weight = AUDIO_WEIGHT_VH; //TODO have this in the settings
|
|
|
|
f32 weightScaling, otherWeightScaling;
|
|
|
|
if(!FPIsFinite(collisionEvent.impactMag))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// The thruster rests on some spindly legs, so big collisions against the base sound wrong - just rely on suspension sounds
|
|
if(m_Parent && m_Parent->GetVehicleModelNameHash() == ATSTRINGHASH("Thruster", 0x58CDAF30) && impactContext->GetTypeFlag(AUD_VEH_COLLISION_BOTTOM))
|
|
{
|
|
return;
|
|
}
|
|
|
|
f32 impactMag = collisionEvent.impactMag;
|
|
f32 otherImpactMag = collisionEvent.impactMag;
|
|
|
|
f32 impactVel = impactContext->impactVelocity;
|
|
|
|
naAssert(impactContext->parent->GetIsTypeVehicle());
|
|
|
|
bool otherIsVehicle = false;
|
|
|
|
if( otherEntity && otherEntity->GetIsTypePed())
|
|
{
|
|
CPed * ped = (CPed *)otherEntity;
|
|
if(GetVehicle() == ped->GetVehiclePedInside())
|
|
{
|
|
return;
|
|
}
|
|
if(impactVel > g_CarVelForPedImpact)
|
|
{
|
|
audPedAudioEntity * pedAudio = ((CPed*)otherEntity)->GetPedAudioEntity();
|
|
|
|
pedAudio->PlayVehicleImpact(posB, GetVehicle(), impactContext);
|
|
pedAudio->PlayUnderOverVehicleSounds(posB, GetVehicle(), impactContext);
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::ShouldRecord())
|
|
{
|
|
CReplayMgr::RecordFx<CPacketVehiclePedCollisionPacket>(
|
|
CPacketVehiclePedCollisionPacket(posB, impactContext), otherEntity, GetParent()->GetOwningEntity());
|
|
}
|
|
#endif
|
|
pedAudio->TriggerImpactSounds(posB, GetVehicleCollisionMaterialSettings(), GetVehicle(), impactVel, collisionEvent.otherComponent, collisionEvent.component, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
CollisionMaterialSettings * materialSettings = GetVehicleCollisionMaterialSettings(collisionEvent.component);
|
|
|
|
if(!materialSettings)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CollisionMaterialSettings * otherMaterialsettings = NULL;
|
|
|
|
bool otherMaterialIsMap = false;
|
|
|
|
if(otherEntity && otherEntity->GetIsTypeVehicle() && !(((CVehicle*)impactContext->otherEntity.Get())->GetVehicleType() == VEHICLE_TYPE_BICYCLE))
|
|
{
|
|
otherIsVehicle = true;
|
|
|
|
VehicleCollisionSettings * otherVehSettings = ((CVehicle*)impactContext->otherEntity.Get())->GetVehicleAudioEntity()->GetCollisionAudio().GetVehicleCollisionSettings();
|
|
|
|
impactContext->impactMag *= otherVehSettings->VehicleSizeScale;
|
|
impactMag = impactContext->impactMag;
|
|
|
|
if(otherVehSettings == settings && settings->VehOnVehCrashSound != g_NullSoundHash)
|
|
{
|
|
f32 otherVel = ((CVehicle*)impactContext->otherEntity.Get())->GetVelocity().Mag();
|
|
if(impactVel > otherVel || (impactVel == otherVel && GetVehicle() > otherEntity))
|
|
{
|
|
m_NeedsToPlayVehOnVehImpact = true;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
otherWeight = AUDIO_WEIGHT_VH; //g_CollisionAudioEntity.GetAudioWeight(impactContext->otherEntity);
|
|
otherWeightScaling = g_CollisionAudioEntity.GetAudioWeightScaling(otherWeight, weight);
|
|
|
|
otherMaterialsettings = ((CVehicle*)impactContext->otherEntity.Get())->GetVehicleAudioEntity()->GetCollisionAudio().GetVehicleCollisionMaterialSettings(collisionEvent.otherComponent);
|
|
}
|
|
else
|
|
{
|
|
otherWeight = g_CollisionAudioEntity.GetAudioWeight(otherEntity);
|
|
|
|
otherMaterialIsMap = (otherWeight == AUDIO_WEIGHT_VH);
|
|
|
|
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(collisionEvent.otherMaterialId);
|
|
|
|
if(unpackedMtlIdA >= PGTAMATERIALMGR->GetNumMaterials())
|
|
{
|
|
return; //Invalid material id bail out
|
|
}
|
|
|
|
if(otherEntity && otherEntity->GetIsTypeObject() && otherEntity->GetBaseModelInfo()->GetModelType() == MI_TYPE_VEHICLE)
|
|
{
|
|
CVehicleModelInfo * vehicleInfo = (CVehicleModelInfo *)(otherEntity->GetBaseModelInfo());
|
|
VehicleCollisionSettings * vehicleCollisions = NULL;
|
|
u32 audioHash = vehicleInfo->GetAudioNameHash();
|
|
|
|
if(!audioHash)
|
|
{
|
|
audioHash = vehicleInfo->GetModelNameHash();
|
|
}
|
|
|
|
if(vehicleInfo->GetVehicleType() == VEHICLE_TYPE_BICYCLE)
|
|
{
|
|
BicycleAudioSettings * bicycleSettings = audNorthAudioEngine::GetObject<BicycleAudioSettings>(audioHash);
|
|
if(bicycleSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(bicycleSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetIsTrain())
|
|
{
|
|
TrainAudioSettings * trainSettings = audNorthAudioEngine::GetObject<TrainAudioSettings>(audioHash);
|
|
if(trainSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(trainSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetIsBoat())
|
|
{
|
|
BoatAudioSettings * boatSettings = audNorthAudioEngine::GetObject<BoatAudioSettings>(audioHash);
|
|
if(boatSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(boatSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetIsPlane())
|
|
{
|
|
PlaneAudioSettings * planeSettings = audNorthAudioEngine::GetObject<PlaneAudioSettings>(audioHash);
|
|
if(planeSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(planeSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetVehicleType() == VEHICLE_TYPE_HELI || vehicleInfo->GetVehicleType()==VEHICLE_TYPE_AUTOGYRO || vehicleInfo->GetVehicleType()==VEHICLE_TYPE_BLIMP)
|
|
{
|
|
HeliAudioSettings * heliSettings = audNorthAudioEngine::GetObject<HeliAudioSettings>(audioHash);
|
|
if(heliSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(heliSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetVehicleType() == VEHICLE_TYPE_TRAILER)
|
|
{
|
|
//Trailers don't have VehicleCollisionSettings and are set up via MACS
|
|
}
|
|
else if(vehicleInfo->GetIsAutomobile() || vehicleInfo->GetVehicleType() == VEHICLE_TYPE_BIKE)
|
|
{
|
|
CarAudioSettings * carSettings = audNorthAudioEngine::GetObject<CarAudioSettings>(audioHash);
|
|
if(carSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(carSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
|
|
if(vehicleCollisions)
|
|
{
|
|
CObject * object = (CObject*)otherEntity;
|
|
if(object->m_nObjectFlags.bCarWheel)
|
|
{
|
|
otherMaterialsettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(vehicleCollisions->WheelFragMaterial);
|
|
}
|
|
else
|
|
{
|
|
otherMaterialsettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(vehicleCollisions->FragMaterial);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!otherMaterialsettings)
|
|
{
|
|
otherMaterialsettings = g_CollisionAudioEntity.GetMaterialOverride(impactContext->otherEntity, g_audCollisionMaterials[(s32)unpackedMtlIdA], collisionEvent.otherComponent);
|
|
}
|
|
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio && otherMaterialsettings)
|
|
{
|
|
naErrorf("Vehicle colliding with %s, raw impact mag %f (event %f)", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(otherMaterialsettings->NameTableOffset), impactContext->impactMag, collisionEvent.impactMag);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if(GetVehicle()->ContainsLocalPlayer())
|
|
{
|
|
if( otherEntity && (otherEntity->GetIsTypeObject() || otherEntity->GetIsTypePed() || otherEntity->GetIsTypeVehicle()))
|
|
{
|
|
g_CollisionAudioEntity.HandlePlayerEvent(otherEntity);
|
|
}
|
|
}
|
|
|
|
weightScaling = g_CollisionAudioEntity.GetAudioWeightScaling(weight, otherWeight);
|
|
otherWeightScaling = g_CollisionAudioEntity.GetAudioWeightScaling(otherWeight, weight);
|
|
|
|
f32 volume = g_SilenceVolume, otherVolume = g_SilenceVolume;
|
|
u32 startOffset = 0, otherStartOffset = 0;
|
|
|
|
f32 collisionMin = otherIsVehicle ? settings->VehicleCollisionMin : settings->CollisionMin;
|
|
f32 collisionMax = otherIsVehicle ? settings->VehicleCollisionMax : settings->CollisionMax;
|
|
|
|
if(impactContext->GetTypeFlag(AUD_VEH_COLLISION_UPSIDEDOWN))
|
|
{
|
|
impactMag *= g_UpsideDownCarImpulseScalingFactor;
|
|
}
|
|
|
|
audCurve curve;
|
|
if(otherIsVehicle ? curve.Init(settings->VehicleImpactStartOffsetCurve) : curve.Init(settings->ImpactStartOffsetCurve))
|
|
{
|
|
startOffset = (u32)floorf(curve.CalculateRescaledValue(0.f, 1.f, collisionMin, collisionMax, impactMag) * 100.0f);
|
|
}
|
|
|
|
f32 volumeLinear = 0;
|
|
|
|
if(curve.Init(settings->VelocityImpactScalingCurve))
|
|
{
|
|
impactMag*=curve.CalculateValue(impactVel);
|
|
}
|
|
|
|
if(otherIsVehicle ? curve.Init(settings->VehicleImpactVolCurve) : curve.Init(settings->ImpactVolCurve))
|
|
{
|
|
volumeLinear = curve.CalculateRescaledValue(0.f, 1.f, collisionMin, collisionMax, impactMag);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear*weightScaling);
|
|
}
|
|
|
|
otherImpactMag *= otherWeightScaling;
|
|
|
|
if(otherEntity && otherEntity->GetCurrentPhysicsInst() && otherEntity->GetCurrentPhysicsInst()->GetClassType() == PH_INST_MAPCOL)
|
|
{
|
|
otherVolume = volume;
|
|
otherStartOffset = startOffset;
|
|
}
|
|
else
|
|
{
|
|
g_CollisionAudioEntity.ComputeStartOffsetAndVolumeFromImpactMagnitude(otherImpactMag, otherMaterialsettings, otherStartOffset, otherVolume);
|
|
|
|
}
|
|
|
|
if(otherEntity && otherEntity->GetIsTypeObject())
|
|
{
|
|
if(((CObject*)otherEntity)->m_nObjectFlags.bHasBeenUprooted && (((((CObject*)otherEntity)->m_nEndOfLifeTimer) >
|
|
(fwTimer::GetTimeInMilliseconds() - 100))))
|
|
{
|
|
otherVolume = 0.f;
|
|
otherStartOffset = 0;
|
|
}
|
|
}
|
|
|
|
// Suppress vehicle/ground collision sounds when adjusting the vehicle hydraulics
|
|
if(GetParent()->HydraulicsModifiedRecently() && GetParent()->GetCachedVelocity().Mag2() < 2.5f)
|
|
{
|
|
if(otherEntity && !otherEntity->GetIsTypeObject() && !otherEntity->GetIsTypePed() && !otherEntity->GetIsTypeVehicle())
|
|
{
|
|
otherVolume = g_SilenceVolume;
|
|
volume = g_SilenceVolume;
|
|
}
|
|
}
|
|
|
|
if((volume <= g_SilenceVolume || startOffset >= 100) && (otherVolume <= g_SilenceVolume || otherStartOffset >= 100))
|
|
{
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naErrorf("Bailing from too-quiet vehicle collision, volume %f, startoffset %u, time %u", volume, startOffset, fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
bool canPlay = true, otherCanPlay = true;
|
|
if(!GetVehicle()->ContainsLocalPlayer() && materialSettings->CollisionCount >= g_CollisionMaterialCountThreshold)
|
|
{
|
|
if(volume > materialSettings->VolumeThreshold)
|
|
{
|
|
materialSettings->VolumeThreshold = volume;
|
|
}
|
|
else
|
|
{
|
|
volume = -100.f;
|
|
canPlay = false;
|
|
}
|
|
}
|
|
|
|
if(volume > impactContext->impactVolume)
|
|
{
|
|
impactContext->impactVolume = volume;
|
|
}
|
|
|
|
|
|
if(volume > m_VehicleCollisionContext.impactVolume)
|
|
{
|
|
m_VehicleCollisionContext.Init(*impactContext);
|
|
}
|
|
|
|
if(otherMaterialsettings && otherMaterialsettings->CollisionCount >= g_CollisionMaterialCountThreshold)
|
|
{
|
|
if(volume > otherMaterialsettings->VolumeThreshold)
|
|
{
|
|
otherMaterialsettings->VolumeThreshold = volume;
|
|
}
|
|
else
|
|
{
|
|
volume = -100.f;
|
|
otherCanPlay = false;
|
|
}
|
|
}
|
|
|
|
GetParent()->TriggerDynamicImpactSounds(volume);
|
|
|
|
u32 hardImpact = materialSettings->HardImpact;
|
|
u32 hardImpactSlowMo = materialSettings->SlowMoHardImpact;
|
|
u32 solidImpact = materialSettings->SolidImpact;
|
|
|
|
u32 otherHardImpact = otherMaterialsettings ? otherMaterialsettings->HardImpact : 0;
|
|
u32 otherHardImpactSlowMo = otherMaterialsettings ? otherMaterialsettings->SlowMoHardImpact : 0;
|
|
u32 otherSoftImpact = otherMaterialsettings ? otherMaterialsettings->SoftImpact : 0;
|
|
u32 otherSolidImpact = otherMaterialsettings? otherMaterialsettings->SolidImpact: 0;
|
|
|
|
if(otherHardImpact == g_NullSoundHash)
|
|
{
|
|
otherHardImpact = 0;
|
|
otherHardImpactSlowMo = g_NullSoundHash;
|
|
}
|
|
if(otherSolidImpact == g_NullSoundHash)
|
|
{
|
|
otherSolidImpact = 0;
|
|
}
|
|
|
|
if (GetParent()->GetAudioVehicleType() == AUD_VEHICLE_HELI && GetParent()->GetVehicleModelNameHash() == ATSTRINGHASH("SEASPARROW", 0xD4AE63D9) && impactContext->GetTypeFlag(AUD_VEH_COLLISION_BOTTOM))
|
|
{
|
|
hardImpact = ATSTRINGHASH("seasparrow_skid_collision", 0x9E6FA734);
|
|
hardImpactSlowMo = ATSTRINGHASH("seasparrow_skid_collision", 0x9E6FA734);
|
|
otherHardImpact = 0;
|
|
otherHardImpactSlowMo = g_NullSoundHash;
|
|
}
|
|
|
|
if(GetParent()->GetAudioVehicleType() == AUD_VEHICLE_BOAT && (!GetVehicle()->GetIsInWater() || impactContext->GetTypeFlag(AUD_VEH_COLLISION_BOTTOM)) && (!otherEntity || !otherEntity->GetIsTypeVehicle()))
|
|
{
|
|
BoatAudioSettings * boatSettings = ((audBoatAudioEntity*)GetParent())->GetBoatAudioSettings();
|
|
hardImpact = otherHardImpact ? boatSettings->DryLandHardImpact : hardImpact;
|
|
hardImpactSlowMo = otherHardImpact ? g_NullSoundHash : hardImpactSlowMo;
|
|
}
|
|
|
|
if(GetParent()->GetAudioVehicleType() == AUD_VEHICLE_CAR && GetParent()->GetVehicle()->InheritsFromAmphibiousQuadBike() && (((audCarAudioEntity*)GetParent())->IsInAmphibiousBoatMode() || ((CAmphibiousQuadBike*)GetParent()->GetVehicle())->IsWheelsFullyIn()) &&
|
|
(!GetVehicle()->GetIsInWater() || impactContext->GetTypeFlag(AUD_VEH_COLLISION_BOTTOM)) && (!otherEntity || !otherEntity->GetIsTypeVehicle()))
|
|
{
|
|
BoatAudioSettings * boatSettings = ((audCarAudioEntity*)GetParent())->GetAmphibiousBoatSettings();
|
|
|
|
if(boatSettings)
|
|
{
|
|
hardImpact = otherHardImpact ? boatSettings->DryLandHardImpact : hardImpact;
|
|
hardImpactSlowMo = otherHardImpact ? g_NullSoundHash : hardImpactSlowMo;
|
|
}
|
|
}
|
|
|
|
audSoundInitParams otherInitParams;
|
|
|
|
otherInitParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
otherInitParams.StartOffset = otherStartOffset;
|
|
otherInitParams.IsStartOffsetPercentage = true;
|
|
otherInitParams.Volume = otherVolume;
|
|
otherInitParams.Position = posB;
|
|
|
|
const bool isKosatkaCollision = ((GetParent()->GetVehicle() && GetParent()->GetVehicle()->GetModelNameHash() == ATSTRINGHASH("Kosatka", 0x4FAF0D70) && impactContext->otherEntity && impactContext->otherEntity->GetIsTypeVehicle()));
|
|
|
|
if(otherHardImpact && hardImpact) //Only play hard impacts if both materials are hard
|
|
{
|
|
if(isKosatkaCollision || !impactContext->otherEntity || !impactContext->otherEntity->GetIsTypeVehicle())
|
|
{
|
|
#if __BANK
|
|
if(audNorthAudioEngine::IsForcingSlowMoVideoEditor() && otherHardImpactSlowMo != g_NullSoundHash)
|
|
{
|
|
otherHardImpact = otherHardImpactSlowMo;
|
|
}
|
|
#endif
|
|
|
|
GetParent()->CreateAndPlaySound(otherHardImpact, &otherInitParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(otherHardImpact, &otherInitParams, GetParent()->GetOwningEntity(), NULL, eNoGlobalSoundEntity, otherHardImpactSlowMo));
|
|
|
|
audNorthAudioEngine::GetGtaEnvironment()->SpikeResonanceObjects(collisionEvent.impactMag * naEnvironment::sm_VehicleCollisionResonanceImpulse, VEC3V_TO_VECTOR3(collisionEvent.pos), naEnvironment::sm_VehicleCollisionResonanceDistance);
|
|
}
|
|
}
|
|
else if(solidImpact && otherSolidImpact)
|
|
{
|
|
if(!impactContext->otherEntity || !impactContext->otherEntity->GetIsTypeVehicle())
|
|
{
|
|
GetParent()->CreateAndPlaySound(otherSolidImpact, &otherInitParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(otherSolidImpact, &otherInitParams, GetParent()->GetOwningEntity()));
|
|
}
|
|
}
|
|
#if __BANK
|
|
PF_INCREMENT(CollisonsProcessed);
|
|
#endif
|
|
|
|
if(impactContext->otherEntity && !impactContext->otherEntity->GetIsTypeVehicle())
|
|
{
|
|
GetParent()->CreateAndPlaySound(otherSoftImpact, &otherInitParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(otherSoftImpact, &otherInitParams, GetParent()->GetOwningEntity()));
|
|
}
|
|
|
|
if(otherMaterialsettings && otherCanPlay && g_UseCollisionIntensityCulling)
|
|
{
|
|
if(!g_RecentlyCollidedMaterials.IsMemberOfList(otherMaterialsettings))
|
|
{
|
|
g_RecentlyCollidedMaterials.Add(otherMaterialsettings);
|
|
}
|
|
otherMaterialsettings->CollisionCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void audVehicleCollisionAudio::ProcessVehicleCollision()
|
|
{
|
|
u32 now = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
if( m_VehicleCollisionContext.impactVolume > -100.f )
|
|
{
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naWarningf("Processing vehicle collision, volume %f vehicle %p", m_VehicleCollisionContext.impactVolume, GetVehicle());
|
|
}
|
|
#endif
|
|
|
|
|
|
if(now - m_LastFakeImpactTime < g_BlockingTimeForVehicleCollisions && m_LastFakeImpactVol > m_VehicleCollisionContext.impactVolume)
|
|
{
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naWarningf("Bailing from vehicle collision due to previous fake collision: volume %f, fake vol %f, last fake time %u, time %u", m_VehicleCollisionContext.impactVolume, m_LastFakeImpactVol, m_LastFakeImpactTime , fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
if(now - m_LastVehicleCollisionTime < g_BlockingTimeForVehicleCollisions && m_LastVehicleCollisionVolume > m_VehicleCollisionContext.impactVolume)
|
|
{
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naWarningf("Bailing from vehicle collision due to previous vehicle collision: volume %f, prev vol %f, prev time %u, time %u", m_VehicleCollisionContext.impactVolume, m_LastVehicleCollisionVolume, m_LastVehicleCollisionTime , fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
const audVehicleCollisionContext & impactContext = m_VehicleCollisionContext;
|
|
const audVehicleCollisionEvent & collisionEvent = m_VehicleCollisionContext.collisionEvent;
|
|
|
|
VehicleCollisionSettings * settings = GetVehicleCollisionSettings();
|
|
|
|
CEntity * otherEntity = impactContext.otherEntity;
|
|
|
|
Vector3 posA = VEC3V_TO_VECTOR3(collisionEvent.pos);
|
|
|
|
CollisionMaterialSettings * materialSettings = GetVehicleCollisionMaterialSettings(collisionEvent.component);
|
|
|
|
CollisionMaterialSettings * otherMaterialsettings = NULL;
|
|
|
|
if(otherEntity && otherEntity->GetIsTypeVehicle())
|
|
{
|
|
otherMaterialsettings = ((CVehicle*)impactContext.otherEntity.Get())->GetVehicleAudioEntity()->GetCollisionAudio().GetVehicleCollisionMaterialSettings(collisionEvent.otherComponent);
|
|
}
|
|
else
|
|
{
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(collisionEvent.otherMaterialId);
|
|
|
|
if(unpackedMtlIdA >= PGTAMATERIALMGR->GetNumMaterials())
|
|
{
|
|
return; //Invalid material id bail out
|
|
}
|
|
|
|
otherMaterialsettings = g_CollisionAudioEntity.GetMaterialOverride(impactContext.otherEntity, g_audCollisionMaterials[(s32)unpackedMtlIdA], collisionEvent.otherComponent);
|
|
}
|
|
|
|
|
|
f32 volume = impactContext.impactVolume;
|
|
u32 startOffset = impactContext.impactOffset;
|
|
|
|
u32 hardImpact = materialSettings->HardImpact;
|
|
u32 hardImpactSlowMo = materialSettings->SlowMoHardImpact;
|
|
|
|
u32 softImpact = materialSettings->SoftImpact;
|
|
u32 solidImpact = materialSettings->SolidImpact;
|
|
|
|
u32 otherHardImpact = otherMaterialsettings ? otherMaterialsettings->HardImpact : 0;
|
|
u32 otherHardImpactSlowMo = otherMaterialsettings ? otherMaterialsettings->SlowMoHardImpact : 0;
|
|
u32 otherSolidImpact = otherMaterialsettings? otherMaterialsettings->SolidImpact: 0;
|
|
|
|
if(otherHardImpact == g_NullSoundHash || (otherEntity && otherEntity->GetIsTypePed()))
|
|
{
|
|
otherHardImpact = 0;
|
|
otherHardImpactSlowMo = 0;
|
|
}
|
|
if(otherSolidImpact == g_NullSoundHash && ((otherEntity && !otherEntity->GetIsTypePed()) || !g_PedsMakeSolidCollisions))
|
|
{
|
|
otherSolidImpact = 0;
|
|
}
|
|
|
|
if (GetParent()->GetAudioVehicleType() == AUD_VEHICLE_HELI && GetParent()->GetVehicleModelNameHash() == ATSTRINGHASH("SEASPARROW", 0xD4AE63D9) && impactContext.GetTypeFlag(AUD_VEH_COLLISION_BOTTOM))
|
|
{
|
|
hardImpact = ATSTRINGHASH("seasparrow_skid_collision", 0x9E6FA734);
|
|
hardImpactSlowMo = ATSTRINGHASH("seasparrow_skid_collision", 0x9E6FA734);
|
|
otherHardImpact = 0;
|
|
otherHardImpactSlowMo = g_NullSoundHash;
|
|
}
|
|
|
|
if(GetParent()->GetAudioVehicleType() == AUD_VEHICLE_BOAT && (!GetVehicle()->GetIsInWater() || impactContext.GetTypeFlag(AUD_VEH_COLLISION_BOTTOM)) && (!otherEntity || !otherEntity->GetIsTypeVehicle()))
|
|
{
|
|
BoatAudioSettings * boatSettings = ((audBoatAudioEntity*)GetParent())->GetBoatAudioSettings();
|
|
hardImpact = otherHardImpact ? boatSettings->DryLandHardImpact : hardImpact;
|
|
hardImpactSlowMo = otherHardImpact ? g_NullSoundHash : hardImpactSlowMo;
|
|
}
|
|
if(GetParent()->GetAudioVehicleType() == AUD_VEHICLE_CAR && GetParent()->GetVehicle()->InheritsFromAmphibiousQuadBike() && (((audCarAudioEntity*)GetParent())->IsInAmphibiousBoatMode() || ((CAmphibiousQuadBike*)GetParent()->GetVehicle())->IsWheelsFullyIn()) &&
|
|
(!GetVehicle()->GetIsInWater() || impactContext.GetTypeFlag(AUD_VEH_COLLISION_BOTTOM)) && (!otherEntity || !otherEntity->GetIsTypeVehicle()))
|
|
{
|
|
BoatAudioSettings * boatSettings = ((audCarAudioEntity*)GetParent())->GetAmphibiousBoatSettings();
|
|
|
|
if(boatSettings)
|
|
{
|
|
hardImpact = otherHardImpact ? boatSettings->DryLandHardImpact : hardImpact;
|
|
hardImpactSlowMo = otherHardImpact ? g_NullSoundHash : hardImpactSlowMo;
|
|
}
|
|
}
|
|
else if(m_NeedsToPlayVehOnVehImpact && settings->VehOnVehCrashSound && settings->VehOnVehCrashSound != g_NullSoundHash)
|
|
{
|
|
hardImpact = settings->VehOnVehCrashSound;
|
|
hardImpactSlowMo = g_NullSoundHash;
|
|
}
|
|
|
|
audSoundInitParams initParams;
|
|
initParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
initParams.StartOffset = startOffset;
|
|
initParams.IsStartOffsetPercentage = true;
|
|
initParams.Volume = volume;
|
|
initParams.Position = posA;
|
|
|
|
//Prevent planes from making too-big collisions under water.
|
|
if(Water::IsCameraUnderwater())
|
|
{
|
|
if(GetVehicle()->GetVehicleType() == VEHICLE_TYPE_PLANE)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!m_Parent->IsToyCar() && otherEntity && otherEntity->GetIsTypeVehicle())
|
|
{
|
|
if (((CVehicle*)otherEntity)->GetVehicleAudioEntity()->IsToyCar())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(otherHardImpact || !otherEntity || otherEntity->GetIsTypeBuilding()) //Only play hard impacts if both materials are hard or if hitting a building
|
|
{
|
|
if(hardImpact)
|
|
{
|
|
if(!g_NoVehicleCollisions && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
#if __BANK
|
|
if(audNorthAudioEngine::IsForcingSlowMoVideoEditor() && hardImpactSlowMo != g_NullSoundHash)
|
|
{
|
|
hardImpact = hardImpactSlowMo;
|
|
}
|
|
#endif
|
|
|
|
GetParent()->CreateAndPlaySound(hardImpact, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(hardImpact, &initParams, GetParent()->GetOwningEntity(), NULL, eNoGlobalSoundEntity, hardImpactSlowMo));
|
|
}
|
|
m_LastVehicleCollisionVolume = volume;
|
|
m_LastVehicleCollisionTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
}
|
|
else if(solidImpact && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
GetParent()->CreateAndPlaySound(solidImpact, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(solidImpact, &initParams, GetParent()->GetOwningEntity()));
|
|
}
|
|
}
|
|
else if(solidImpact && otherSolidImpact && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
GetParent()->CreateAndPlaySound(solidImpact, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(solidImpact, &initParams, GetParent()->GetOwningEntity()));
|
|
}
|
|
#if __BANK
|
|
PF_INCREMENT(CollisonsProcessed);
|
|
#endif
|
|
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naErrorf("Process Collision: impactMag %f, volume %f, time %u", impactContext.impactMag, volume, fwTimer::GetTimeInMilliseconds());
|
|
naErrorf("Vehicle collision sounds: hardImpact %s, solidImpact %s, softImpact %s, vehicle %s", g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(hardImpact), g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(solidImpact), g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(softImpact), GetVehicle()->GetModelName());
|
|
naErrorf("Other collision sounds: hardImpact %s, solidImpact %s entity %s", g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(otherHardImpact), g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(otherSolidImpact), otherEntity ? otherEntity->GetModelName() : NULL);
|
|
grcDebugDraw::Sphere(GetVehicle()->GetTransform().GetPosition() + Vec3V(ScalarV(0.f), ScalarV(0.f), ScalarV(2.f)), 0.5f, Color32(0,255, 255, (int)(audDriverUtil::ComputeLinearVolumeFromDb(volume)*255)), true, 5);
|
|
}
|
|
#endif
|
|
if( g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
GetParent()->CreateAndPlaySound(softImpact, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(softImpact, &initParams, GetParent()->GetOwningEntity()));
|
|
}
|
|
|
|
f32 sweetenerTheshold = impactContext.GetTypeFlag(AUD_VEH_COLLISION_ON_VEHICLE) ? settings->VehicleSweetenerThreshold : settings->SweetenerImpactThreshold;
|
|
|
|
// If the conductors are playing the stunt jump stuff, we don't want to play the big impact as we are using a custom version.
|
|
// PlayStuntJumpCollision will return true when it's going to play the custom impact, so we have to stop playing the high impact sweetener as usual
|
|
if(!NetworkInterface::IsGameInProgress() && !SUPERCONDUCTOR.GetVehicleConductor().PlayStuntJumpCollision(impactContext.impactMag))
|
|
{
|
|
if(impactContext.impactMag >= sweetenerTheshold && GetVehicle()->ContainsLocalPlayer() && !g_NoCrashSweetener && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naErrorf("Vehicle impact sweetener, time %u", fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
#endif
|
|
GetParent()->CreateAndPlaySound(GetVehicleCollisionSettings()->HighImpactSweetenerSound, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(GetVehicleCollisionSettings()->HighImpactSweetenerSound, &initParams, GetParent()->GetOwningEntity()));
|
|
if(otherMaterialsettings)
|
|
{
|
|
GetParent()->CreateAndPlaySound(otherMaterialsettings->BigVehicleImpactSound, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(otherMaterialsettings->BigVehicleImpactSound, &initParams, GetParent()->GetOwningEntity()));
|
|
}
|
|
if(g_FrontendAudioEntity.GetSpecialAbilityMode() == audFrontendAudioEntity::kSpecialAbilityModeFranklin)
|
|
{
|
|
GetParent()->CreateAndPlaySound(g_FrontendAudioEntity.GetSpecialAbilitySounds().Find(ATSTRINGHASH("Franklin_VehicleCrash", 0xE22DC8F9)), &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(g_FrontendAudioEntity.GetSpecialAbilitySounds().Find(ATSTRINGHASH("Franklin_VehicleCrash", 0xE22DC8F9)).Get(), &initParams, GetParent()->GetOwningEntity()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if(materialSettings && g_UseCollisionIntensityCulling)
|
|
{
|
|
if(!g_RecentlyCollidedMaterials.IsMemberOfList(materialSettings))
|
|
{
|
|
g_RecentlyCollidedMaterials.Add(materialSettings);
|
|
}
|
|
materialSettings->CollisionCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool audVehicleCollisionAudio::IsTrailer()
|
|
{
|
|
if(GetVehicle())
|
|
{
|
|
return ((CVehicleModelInfo*)GetVehicle()->GetBaseModelInfo())->GetIsTrailer();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool audVehicleCollisionAudio::IsBike()
|
|
{
|
|
if(GetVehicle())
|
|
{
|
|
return ((CVehicleModelInfo*)GetVehicle()->GetBaseModelInfo())->GetIsBike();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool audVehicleCollisionAudio::IsBoat()
|
|
{
|
|
if(GetVehicle())
|
|
{
|
|
return ((CVehicleModelInfo*)GetVehicle()->GetBaseModelInfo())->GetIsBoat();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CollisionMaterialSettings * audVehicleCollisionAudio::GetVehicleCollisionMaterialSettings()
|
|
{
|
|
if(BANK_ONLY(!sm_UpdateVehicleCollisionSettings &&) m_CollisionMaterialSettings)
|
|
{
|
|
return m_CollisionMaterialSettings;
|
|
}
|
|
|
|
VehicleCollisionSettings *settings = GetVehicleCollisionSettings();
|
|
|
|
if(settings)
|
|
{
|
|
m_CollisionMaterialSettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(settings->VehicleMaterialSettings);
|
|
}
|
|
else
|
|
{
|
|
m_CollisionMaterialSettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_CAR_ENTITY", 0x22F79CB));
|
|
}
|
|
|
|
return m_CollisionMaterialSettings;
|
|
}
|
|
|
|
CollisionMaterialSettings * audVehicleCollisionAudio::GetVehicleCollisionMaterialSettings(u32 component)
|
|
{
|
|
if(IsTrailer())
|
|
{
|
|
CollisionMaterialSettings* collisionSettings = g_CollisionAudioEntity.GetMaterialOverride(GetVehicle(), NULL, component);
|
|
if(collisionSettings)
|
|
{
|
|
return collisionSettings;
|
|
}
|
|
}
|
|
|
|
if(BANK_ONLY(!sm_UpdateVehicleCollisionSettings &&) m_CollisionMaterialSettings)
|
|
{
|
|
return m_CollisionMaterialSettings;
|
|
}
|
|
|
|
VehicleCollisionSettings *settings = GetVehicleCollisionSettings();
|
|
|
|
m_CollisionMaterialSettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(settings->VehicleMaterialSettings);
|
|
return m_CollisionMaterialSettings;
|
|
}
|
|
|
|
void audVehicleCollisionAudio::HeadLightSmash()
|
|
{
|
|
if(GetParent()->GetAudioVehicleType() == AUD_VEHICLE_BOAT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(GetParent()->GetDrowningFactor() >= 1.f && !Water::IsCameraUnderwater())
|
|
{
|
|
return;
|
|
}
|
|
|
|
audCurve headlightCurve;
|
|
|
|
headlightCurve.Init(ATSTRINGHASH("veh_headlight_vel_to_impactmag", 0x1ACFCF7C));
|
|
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
grcDebugDraw::Sphere(GetVehicle()->GetTransform().GetPosition() + GetVehicle()->GetTransform().GetB() + Vec3V(ScalarV(0.f), ScalarV(0.f), ScalarV(1.5f)), 0.5f, Color32(255, 0, 0), true, 5);
|
|
}
|
|
#endif
|
|
|
|
//@@: location AUDVEHICLECOLLISIONAUDO_HEADLIGHTSMASH_CALC_MAG
|
|
float impactMag = headlightCurve.CalculateValue(GetParent()->GetVehicle()->GetVelocity().Mag());
|
|
|
|
Fakeimpact(impactMag);
|
|
}
|
|
|
|
void audVehicleCollisionAudio::Fakeimpact(f32 impactMag, f32 UNUSED_PARAM(volumeCurveScale), bool forceImpact)
|
|
{
|
|
VehicleCollisionSettings * settings = GetVehicleCollisionSettings();
|
|
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naErrorf("Registering fake impact impactMag %f, time %u", impactMag, fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
#endif
|
|
|
|
if(naVerifyf(settings, "Couldn't get vehicle settings for %s", m_Parent->GetVehicle()->GetDebugName()) && (m_ImpactThisFrame||forceImpact))
|
|
{
|
|
m_FakeImpactMagThisFrame = Max(impactMag, m_FakeImpactMagThisFrame);
|
|
}
|
|
}
|
|
|
|
void audVehicleCollisionAudio::ProcessFakeImpact()
|
|
{
|
|
VehicleCollisionSettings * settings = GetVehicleCollisionSettings();
|
|
|
|
if(g_NoFakeVehicleCollisions)
|
|
{
|
|
return;
|
|
}
|
|
if(GetParent()->GetDrowningFactor() >= 1.f && !Water::IsCameraUnderwater())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(m_FakeImpactMagThisFrame > 0.f)
|
|
{
|
|
u32 startOffset = 0;
|
|
float volume = -100;
|
|
|
|
audCurve curve;
|
|
|
|
f32 impactMag = m_FakeImpactMagThisFrame;
|
|
|
|
if(curve.Init(settings->VelocityImpactScalingCurve))
|
|
{
|
|
impactMag*=curve.CalculateValue(GetVehicle()->GetVelocity().Mag());
|
|
}
|
|
|
|
if(curve.Init(settings->FakeImpactStartOffsetCurve))
|
|
{
|
|
startOffset = (u32)floorf(curve.CalculateRescaledValue(0.f, 1.f, settings->FakeImpactMin, settings->FakeImpactMax, impactMag) * 100.0f);
|
|
}
|
|
|
|
f32 volumeLinear = 0;
|
|
if(curve.Init((settings->FakeImpactVolCurve)))
|
|
{
|
|
volumeLinear = curve.CalculateRescaledValue(0.f, 1.f, settings->FakeImpactMin, settings->FakeImpactMax, impactMag);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
|
|
u32 now = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
if(((volume <= (m_LastVehicleCollisionVolume + settings->FakeImpactTriggerDelta)) &&
|
|
(now - m_LastVehicleCollisionTime < g_BlockingTimeForFakeImpacts)) && !g_AlwaysPlayFakeCollisions)
|
|
{
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naErrorf("Bailing from fake impact (collision), mag %f, volume %f, lastVolume %f, time %u, lasttime %u", impactMag, volume, m_LastFakeImpactVol, now, m_LastFakeImpactTime);
|
|
}
|
|
#endif
|
|
m_LastFakeImpactTime = now;
|
|
return;
|
|
}
|
|
|
|
if(((volume <= m_LastFakeImpactVol) &&
|
|
(now - m_LastFakeImpactTime < g_BlockingTimeForFakeImpacts)) && !g_AlwaysPlayFakeCollisions)
|
|
{
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naErrorf("Bailing from fake impact (fake impact), mag %f, volume %f, lastVolume %f, time %u, lasttime %u", impactMag, volume, m_LastFakeImpactVol, now, m_LastFakeImpactTime);
|
|
}
|
|
#endif
|
|
m_LastFakeImpactTime = now;
|
|
return;
|
|
}
|
|
|
|
m_LastFakeImpactVol = volume;
|
|
m_LastFakeImpactTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naErrorf("Fake impact: impactMag %f, Vol %f, time %u", impactMag, volume, fwTimer::GetTimeInMilliseconds());
|
|
grcDebugDraw::Sphere(GetVehicle()->GetTransform().GetPosition() + GetVehicle()->GetTransform().GetA() + Vec3V(ScalarV(0.f), ScalarV(0.f), ScalarV(1.5f)), 0.5f, Color32(255,255,0, (int)(volumeLinear*255)), true, 5);
|
|
}
|
|
#endif
|
|
|
|
audSoundInitParams initParams;
|
|
initParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
initParams.StartOffset = startOffset;
|
|
initParams.IsStartOffsetPercentage = true;
|
|
initParams.Volume = volume;
|
|
initParams.Tracker = GetVehicle()->GetPlaceableTracker();
|
|
if(sm_VehicleCollisionRolloffBoostCurve.IsValid())
|
|
{
|
|
initParams.VolumeCurveScale *= sm_VehicleCollisionRolloffBoostCurve.CalculateValue(impactMag);
|
|
if(g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) - GetParent()->GetLastTimeExploded() < g_VehicleExplosionAudioBoostTime)
|
|
{
|
|
initParams.VolumeCurveScale += g_VehicleExplosionCollisionAudioRolloffBoost;
|
|
initParams.Volume += g_VehicleExplosionCollisionAudioVolumeBoost;
|
|
}
|
|
}
|
|
|
|
if(GetParent()->GetDrowningFactor() >= 1.f)
|
|
{
|
|
if(GetVehicle()->GetVehicleType() == VEHICLE_TYPE_PLANE)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// If the conductors are playing the stunt jump stuff, we don't want to play the big impact as we are using a custom version.
|
|
// PlayStuntJumpCollision will return true when it's going to play the custom impact, so we have to stop playing the high impact sweetener as usual
|
|
if(!SUPERCONDUCTOR.GetVehicleConductor().PlayStuntJumpCollision(impactMag))
|
|
{
|
|
if(impactMag >= settings->FakeImpactSweetenerThreshold && GetVehicle()->ContainsLocalPlayer() && ! g_NoCrashSweetener && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
naErrorf("Vehicle impact sweetener, time %u", fwTimer::GetTimeInMilliseconds());
|
|
grcDebugDraw::Sphere(GetVehicle()->GetTransform().GetPosition() + GetVehicle()->GetTransform().GetA() + Vec3V(ScalarV(0.f), ScalarV(0.5f), ScalarV(1.5f)), 0.5f, Color32(255,255, 100, (int)(volumeLinear*255)), true, 5);
|
|
}
|
|
#endif
|
|
GetParent()->CreateAndPlaySound(GetVehicleCollisionSettings()->HighImpactSweetenerSound, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(GetVehicleCollisionSettings()->HighImpactSweetenerSound, &initParams, GetParent()->GetOwningEntity(), GetVehicle()));
|
|
if(g_FrontendAudioEntity.GetSpecialAbilityMode() == audFrontendAudioEntity::kSpecialAbilityModeFranklin)
|
|
{
|
|
GetParent()->CreateAndPlaySound(g_FrontendAudioEntity.GetSpecialAbilitySounds().Find(ATSTRINGHASH("Franklin_VehicleCrash", 0xE22DC8F9)), &initParams);
|
|
}
|
|
}
|
|
}
|
|
if(g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
CollisionMaterialSettings* materialSettings = GetVehicleCollisionMaterialSettings();
|
|
|
|
if(materialSettings)
|
|
{
|
|
u32 vehicleHardImpact = materialSettings->HardImpact;
|
|
|
|
#if __BANK || GTA_REPLAY
|
|
u32 vehicleHardImpactSlowMo = materialSettings->SlowMoHardImpact;
|
|
|
|
#if __BANK
|
|
if(audNorthAudioEngine::IsForcingSlowMoVideoEditor() && vehicleHardImpactSlowMo != g_NullSoundHash)
|
|
{
|
|
vehicleHardImpact = vehicleHardImpactSlowMo;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
GetParent()->CreateAndPlaySound(vehicleHardImpact, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(vehicleHardImpact, &initParams, GetParent()->GetOwningEntity(), GetVehicle(), eNoGlobalSoundEntity, vehicleHardImpactSlowMo));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void audVehicleCollisionAudio::PostProcessImpacts()
|
|
{
|
|
if(!GetVehicle()->IsDummy() && !GetParent()->GetIsWaitingToInit())
|
|
{
|
|
if(!GetParent()->GetVehicle()->InheritsFromSubmarine() && GetParent()->GetDrowningFactor() >= 1.f && !Water::IsCameraUnderwater())
|
|
{
|
|
return;
|
|
}
|
|
|
|
VehicleCollisionSettings *settings = GetVehicleCollisionSettings();
|
|
|
|
CollisionMaterialSettings * materialSettings = GetVehicleCollisionMaterialSettings();
|
|
|
|
if(!materialSettings)
|
|
{
|
|
return;
|
|
}
|
|
|
|
audVehicleCollisionContext * playContext = m_CollisionEventsList.GetEvents();
|
|
f32 scrapeMag = 0.f, impactVolume = 0.f;
|
|
f32 impactVelocity = 0.f, upsideDownMag = 0.f;
|
|
bool didCollision = false;
|
|
bool isBottomScrape = false;
|
|
audVehicleCollisionContext * scrapeContext = NULL;
|
|
|
|
while(playContext)
|
|
{
|
|
if(playContext->canPlayCollision)
|
|
{
|
|
ProcessCollision(playContext, playContext->collisionEvent);
|
|
playContext->canPlayCollision = false;
|
|
didCollision = true;
|
|
}
|
|
|
|
if(playContext->GetTypeFlag(AUD_VEH_COLLISION_SCRAPE))
|
|
{
|
|
if(playContext->scrapeMag > scrapeMag)
|
|
{
|
|
isBottomScrape = playContext->GetTypeFlag(AUD_VEH_COLLISION_BOTTOM);
|
|
scrapeMag = playContext->scrapeMag;
|
|
scrapeContext = playContext;
|
|
}
|
|
}
|
|
|
|
f32 velocity = playContext->velocity.Mag();
|
|
if(velocity > impactVelocity)
|
|
{
|
|
impactVelocity = velocity;
|
|
impactVolume = playContext->impactVolume;
|
|
}
|
|
|
|
if(playContext->GetTypeFlag(AUD_VEH_COLLISION_UPSIDEDOWN) && playContext->impactMag > upsideDownMag)
|
|
{
|
|
upsideDownMag = playContext->impactMag;
|
|
}
|
|
|
|
playContext = playContext->next;
|
|
}
|
|
|
|
Vector3 pos = VEC3V_TO_VECTOR3(GetVehicle()->GetTransform().GetPosition());
|
|
|
|
|
|
if(didCollision && m_WreckedTime + 1000 > g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
audSoundInitParams wreckedParams;
|
|
wreckedParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
wreckedParams.Position = pos;
|
|
GetParent()->CreateAndPlaySound(sm_SoundSet.Find(ATSTRINGHASH("wrecked_crash", 0xB573616C)), &wreckedParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(sm_SoundSet.Find(ATSTRINGHASH("wrecked_crash", 0xB573616C)).Get(), &wreckedParams, GetParent()->GetOwningEntity()));
|
|
}
|
|
|
|
ProcessFoliage();
|
|
|
|
audSoundInitParams damageParams;
|
|
damageParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
damageParams.Volume = impactVolume;
|
|
//damageParams.Position = &GetVehicle()->GetPosition();
|
|
damageParams.Tracker = GetVehicle()->GetPlaceableTracker();
|
|
|
|
f32 bodyDamageFactor = Max(m_Parent->GetScriptBodyDamageFactor(), 1.0f - (GetVehicle()->GetVehicleDamage()->GetBodyHealth() / CVehicleDamage::GetBodyHealthMax()));
|
|
|
|
if(didCollision)
|
|
{
|
|
if(bodyDamageFactor > g_BodyDamageFactorForDebris)
|
|
{
|
|
if (impactVelocity > g_DamageImpactVel)
|
|
{
|
|
if(g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
GetParent()->CreateAndPlaySound(settings->ImpactDebris, &damageParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(settings->ImpactDebris, &damageParams, GetParent()->GetOwningEntity(), GetVehicle()));
|
|
}
|
|
TriggerDebrisSounds(impactVelocity);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(upsideDownMag > 0.f)
|
|
{
|
|
TriggerUpsideDownRoll(upsideDownMag);
|
|
if(bodyDamageFactor > g_BodyDamageFactorForDebris)
|
|
{
|
|
TriggerDebrisSounds(upsideDownMag);
|
|
}
|
|
}
|
|
|
|
|
|
if(FPIsFinite(scrapeMag) && scrapeMag > materialSettings->MinScrapeSpeed && !g_NoVehicleScrapeAudio)
|
|
{
|
|
f32 volume = -100.f;
|
|
int pitch = 0;
|
|
|
|
naAssertf(FPIsFinite(scrapeMag) &&
|
|
FPIsFinite(materialSettings->MinScrapeSpeed) &&
|
|
FPIsFinite(materialSettings->MaxScrapeSpeed),
|
|
"Hit a NaN when processing vehicle scrapes on %s with material %s, scrapeMag %f, MinScrapeSpeed %f, MaxScrapeSpeed %f",
|
|
GetVehicle()->GetModelName(), audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings->NameTableOffset),
|
|
scrapeMag, materialSettings->MinScrapeSpeed, materialSettings->MaxScrapeSpeed);
|
|
|
|
if(m_ScrapeVolCurve.IsValid())
|
|
{
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(m_ScrapeVolCurve.CalculateRescaledValue(0.f, 1.f, materialSettings->MinScrapeSpeed, materialSettings->MaxScrapeSpeed, scrapeMag));
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Trying to play scrape for vehicle (%s) but volume curve is invalid", GetVehicle()->GetModelName());
|
|
volume = -100;
|
|
}
|
|
|
|
if(m_ScrapePitchCurve.IsValid())
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(m_ScrapePitchCurve.CalculateRescaledValue(0.f, 1.f, materialSettings->MinScrapeSpeed, materialSettings->MaxScrapeSpeed, scrapeMag));
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Trying to play scrape for vehicle (%s) but pitch curve is invalid", GetVehicle()->GetModelName());
|
|
pitch = 0;
|
|
}
|
|
|
|
if(!m_ScrapeSound)
|
|
{
|
|
audSoundInitParams initParams;
|
|
|
|
initParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
|
|
const Vector3 pos = VEC3V_TO_VECTOR3(GetVehicle()->GetTransform().GetPosition());
|
|
initParams.Position = pos;
|
|
|
|
//#if __BANK
|
|
// if(g_DebugVehicleCollisionAudio)
|
|
// {
|
|
// grcDebugDraw::Sphere(GetVehicle()->GetTransform().GetPosition() - GetVehicle()->GetTransform().GetA() + Vec3V(ScalarV(0.0f), ScalarV(0.f), ScalarV(1.5f)), 0.5f, Color32(255, 0, 255), true, 5);
|
|
// }
|
|
//#endif
|
|
u32 scrapeSound = materialSettings->ScrapeSound;
|
|
u32 slowScrapeSound = m_VehicleCollisionSettings->SlowScrapeLoop;
|
|
|
|
bool suppressScrapeImpacts = false;
|
|
|
|
if (GetParent()->GetAudioVehicleType() == AUD_VEHICLE_HELI && GetParent()->GetVehicleModelNameHash() == ATSTRINGHASH("SEASPARROW", 0xD4AE63D9) && isBottomScrape)
|
|
{
|
|
suppressScrapeImpacts = true;
|
|
scrapeSound = ATSTRINGHASH("seasparrow_skid_scrape", 0x323B87AF);
|
|
slowScrapeSound = ATSTRINGHASH("seasparrow_slow_skid_scrape", 0x37B1B8BF);
|
|
}
|
|
|
|
if (!suppressScrapeImpacts)
|
|
{
|
|
TriggerScrapeImpact(initParams);
|
|
}
|
|
|
|
initParams.Volume = volume;
|
|
initParams.Pitch = static_cast<s16>( pitch );
|
|
initParams.u32ClientVar = AUD_VEHICLE_SOUND_SCRAPE;
|
|
initParams.UpdateEntity = true;
|
|
|
|
if(GetParent()->GetAudioVehicleType() == AUD_VEHICLE_BOAT && !GetVehicle()->GetIsInWater())
|
|
{
|
|
|
|
bool hardMaterial = false;
|
|
if(scrapeContext)
|
|
{
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(scrapeContext->collisionEvent.otherMaterialId);
|
|
if(unpackedMtlIdA < PGTAMATERIALMGR->GetNumMaterials())
|
|
{
|
|
CollisionMaterialSettings * scrapeMaterialSettings = g_CollisionAudioEntity.GetMaterialOverride(scrapeContext->otherEntity, g_audCollisionMaterials[(s32)unpackedMtlIdA]);
|
|
if(scrapeMaterialSettings && scrapeMaterialSettings->HardImpact && scrapeMaterialSettings->HardImpact != g_NullSoundHash)
|
|
{
|
|
hardMaterial = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
BoatAudioSettings * boatSettings = ((audBoatAudioEntity*)GetParent())->GetBoatAudioSettings();
|
|
scrapeSound = hardMaterial ? boatSettings->DryLandHardScrape : boatSettings->DryLandScrape;
|
|
}
|
|
|
|
if(GetParent()->GetAudioVehicleType() == AUD_VEHICLE_CAR && GetParent()->GetVehicle()->InheritsFromAmphibiousQuadBike() && (((audCarAudioEntity*)GetParent())->IsInAmphibiousBoatMode() || ((CAmphibiousQuadBike*)GetParent()->GetVehicle())->IsWheelsFullyIn()))
|
|
{
|
|
bool hardMaterial = false;
|
|
if(scrapeContext)
|
|
{
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(scrapeContext->collisionEvent.otherMaterialId);
|
|
if(unpackedMtlIdA < PGTAMATERIALMGR->GetNumMaterials())
|
|
{
|
|
CollisionMaterialSettings * scrapeMaterialSettings = g_CollisionAudioEntity.GetMaterialOverride(scrapeContext->otherEntity, g_audCollisionMaterials[(s32)unpackedMtlIdA]);
|
|
if(scrapeMaterialSettings && scrapeMaterialSettings->HardImpact && scrapeMaterialSettings->HardImpact != g_NullSoundHash)
|
|
{
|
|
hardMaterial = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
BoatAudioSettings * boatSettings = ((audCarAudioEntity*)GetParent())->GetAmphibiousBoatSettings();
|
|
|
|
if(boatSettings)
|
|
{
|
|
scrapeSound = hardMaterial ? boatSettings->DryLandHardScrape : boatSettings->DryLandScrape;
|
|
}
|
|
}
|
|
|
|
// url:bugstar:6794909 - Kosatka - MULTI_SCRAPE is playing when grounding the sub
|
|
if (scrapeSound != ATSTRINGHASH("MULTI_SCRAPE", 0x69751E17) || !GetVehicle() || GetVehicle()->GetModelNameHash() != ATSTRINGHASH("Kosatka", 0x4FAF0D70))
|
|
{
|
|
GetParent()->CreateSound_PersistentReference(scrapeSound, &m_ScrapeSound, &initParams);
|
|
}
|
|
|
|
if(m_ScrapeSound)
|
|
{
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSoundPersistant(scrapeSound, &initParams, m_ScrapeSound, GetParent()->GetOwningEntity(), eNoGlobalSoundEntity);)
|
|
m_ScrapeSound->PrepareAndPlay();
|
|
}
|
|
|
|
if(m_SlowScrapeSound)
|
|
{
|
|
m_SlowScrapeSound->StopAndForget();
|
|
}
|
|
|
|
if(m_SlowScrapeVolCurve.IsValid())
|
|
{
|
|
initParams.Volume = audDriverUtil::ComputeDbVolumeFromLinear(m_SlowScrapeVolCurve.CalculateRescaledValue(0.f, 1.f, materialSettings->MinScrapeSpeed, materialSettings->MaxScrapeSpeed, scrapeMag));
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Trying to play slow scrape for vehicle (%s) but volume curve is invalid", GetVehicle()->GetModelName());
|
|
initParams.Volume = -100;
|
|
}
|
|
|
|
if(scrapeMag < 0.1f && m_Parent->GetCachedVelocity().Mag2() < 1.f)
|
|
{
|
|
if(m_Parent->AreHydraulicsActive())
|
|
{
|
|
initParams.Volume = -100;
|
|
}
|
|
}
|
|
|
|
GetParent()->AllocateVehicleVariableBlock();
|
|
GetParent()->CreateSound_PersistentReference(slowScrapeSound, &m_SlowScrapeSound, &initParams);
|
|
|
|
if(m_SlowScrapeSound)
|
|
{
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSoundPersistant(slowScrapeSound, &initParams, m_SlowScrapeSound, GetParent()->GetOwningEntity(), eNoGlobalSoundEntity);)
|
|
|
|
m_SlowScrapeSound->PrepareAndPlay();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(volume < (m_ScrapeSound->GetRequestedVolume() - 0.003f*(float)m_ScrapeSound->GetSimpleReleaseTime()))
|
|
{
|
|
volume = m_ScrapeSound->GetRequestedVolume() - 0.003f*(float)m_ScrapeSound->GetSimpleReleaseTime();
|
|
}
|
|
|
|
if(pitch < (m_ScrapeSound->GetRequestedPitch() - 0.003f*(float)m_ScrapeSound->GetSimpleReleaseTime()))
|
|
{
|
|
pitch = (s32)(m_ScrapeSound->GetRequestedPitch() - 0.003f*(float)m_ScrapeSound->GetSimpleReleaseTime());
|
|
}
|
|
|
|
m_ScrapeSound->SetRequestedVolume(volume);
|
|
m_ScrapeSound->SetRequestedPitch(pitch);
|
|
|
|
if(m_SlowScrapeVolCurve.IsValid())
|
|
{
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(m_SlowScrapeVolCurve.CalculateRescaledValue(materialSettings->MinScrapeSpeed, materialSettings->MaxScrapeSpeed, 0.f, 1.f, scrapeMag));
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Trying to play slow scrape for vehicle (%s) but volume curve is invalid", GetVehicle()->GetModelName());
|
|
volume = -100;
|
|
}
|
|
|
|
if(m_SlowScrapeSound)
|
|
{
|
|
if(volume < (m_SlowScrapeSound->GetRequestedVolume() - 0.003f*(float)m_SlowScrapeSound->GetSimpleReleaseTime()))
|
|
{
|
|
volume = m_SlowScrapeSound->GetRequestedVolume() - 0.003f*(float)m_SlowScrapeSound->GetSimpleReleaseTime();
|
|
}
|
|
|
|
if(pitch < (m_SlowScrapeSound->GetRequestedPitch() - 0.003f*(float)m_SlowScrapeSound->GetSimpleReleaseTime()))
|
|
{
|
|
pitch = (s32)(m_SlowScrapeSound->GetRequestedPitch() - 0.003f*(float)m_SlowScrapeSound->GetSimpleReleaseTime());
|
|
}
|
|
|
|
m_SlowScrapeSound->SetRequestedVolume(volume);
|
|
m_SlowScrapeSound->SetRequestedPitch(pitch);
|
|
}
|
|
}
|
|
m_ScrapeStopSoundMs = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) + g_ScrapingCollisionHoldTimeMs;
|
|
}
|
|
|
|
|
|
// Vector3 pos;
|
|
//m_Vehicle->TransformIntoWorldSpace(pos, offset);
|
|
|
|
f32 volume = 0.f, dbVol = g_SilenceVolume;
|
|
|
|
if (!m_IsOnAnotherVehicle && m_ImpactDamage >= settings->DamageMin && FPIsFinite(m_ImpactDamage) && !g_NoDeformationAudio)
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(settings->DamageVolCurve))
|
|
{
|
|
volume = curve.CalculateRescaledValue(0.f, 1.f, settings->DamageMin, settings->DamageMax, m_ImpactDamage);
|
|
dbVol = audDriverUtil::ComputeDbVolumeFromLinear(volume);
|
|
}
|
|
|
|
if(dbVol > g_SilenceVolume && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
audSoundInitParams initParams;
|
|
initParams.UpdateEntity = true;
|
|
initParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
initParams.Volume = dbVol;
|
|
//initParams.Position = GetVehicle()->GetPosition();
|
|
initParams.Tracker = GetVehicle()->GetPlaceableTracker();
|
|
GetParent()->CreateAndPlaySound(settings->DeformationSound, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(settings->DeformationSound, &initParams, GetParent()->GetOwningEntity(), GetVehicle()));
|
|
|
|
#if __BANK
|
|
if(g_DebugVehicleCollisionAudio)
|
|
{
|
|
grcDebugDraw::Sphere(GetVehicle()->GetTransform().GetPosition() - GetVehicle()->GetTransform().GetB() + Vec3V(ScalarV(0.f), ScalarV(0.f), ScalarV(1.5f)), 0.5f, Color32(0, 0, 255), true, 5);
|
|
}
|
|
#endif
|
|
|
|
if(impactVelocity > g_DeformationImpactVel)
|
|
{
|
|
u32 hardImpactSoundHash = materialSettings->HardImpact;
|
|
|
|
#if __BANK || GTA_REPLAY
|
|
u32 hardImpactSlowMoSoundHash = materialSettings->SlowMoHardImpact;
|
|
|
|
#if __BANK
|
|
if(audNorthAudioEngine::IsForcingSlowMoVideoEditor() && hardImpactSlowMoSoundHash != g_NullSoundHash)
|
|
{
|
|
hardImpactSoundHash = hardImpactSlowMoSoundHash;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
GetParent()->CreateAndPlaySound(hardImpactSoundHash, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(hardImpactSoundHash, &initParams, GetParent()->GetOwningEntity(), GetVehicle(), eNoGlobalSoundEntity, hardImpactSlowMoSoundHash));
|
|
}
|
|
}
|
|
}
|
|
|
|
f32 numWheelsOnGround = 0.f, numWheelsWereOnGround = 0.f;
|
|
|
|
|
|
for(int i=0; i< GetVehicle()->GetNumWheels(); i++)
|
|
{
|
|
if(GetVehicle()->GetWheel(i)->GetIsTouching())
|
|
{
|
|
numWheelsOnGround += 1.f;
|
|
}
|
|
if(GetVehicle()->GetWheel(i)->GetWasTouching())
|
|
{
|
|
numWheelsWereOnGround += 1.f;
|
|
}
|
|
}
|
|
|
|
u32 now = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
u32 minJumpLandDuration = 0;
|
|
bool useBigJump = true;
|
|
|
|
// See url:bugstar:5492108 - RC Bandito has very spongy suspension and its easy to trigger a jump land sound by cornering fast and momentarily
|
|
// lifting one wheel off the ground. This code just dampens it down a bit so that we also need a minimum air time to register the impact.
|
|
if (m_Parent->IsToyCar() || m_Parent->IsGolfKart())
|
|
{
|
|
if (fwTimer::GetTimeInMilliseconds() - m_Parent->GetLastTimeInAir() < g_ToyCarMinAirTimeForBigLanding)
|
|
{
|
|
useBigJump = true;
|
|
minJumpLandDuration = g_ToyCarMinBigJumpLandDuration;
|
|
}
|
|
else
|
|
{
|
|
useBigJump = false;
|
|
minJumpLandDuration = g_ToyCarMinSmallJumpLandDuration;
|
|
}
|
|
}
|
|
|
|
if(numWheelsOnGround - numWheelsWereOnGround > 0 && numWheelsWereOnGround < 5 && now - m_JumpStartTime > minJumpLandDuration)
|
|
{
|
|
// Don't play jump landing sounds from amphibious quadbikes while we're deploying/retracting the wheels, unless we're genuinely falling from a height
|
|
if(!m_Parent->GetVehicle()->InheritsFromAmphibiousQuadBike() || ((CAmphibiousQuadBike*)m_Parent->GetVehicle())->IsWheelTransitionFinished() || m_Parent->GetCachedVelocity().GetZ() < -5)
|
|
{
|
|
if(m_Parent->GetVehicle()->GetSpecialFlightModeRatio() == 0.0f)
|
|
{
|
|
bool justFinishedWheelie = numWheelsOnGround > 2 && m_Parent->GetVehicle()->InheritsFromAutomobile() && ((CAutomobile*)m_Parent->GetVehicle())->m_nAutomobileFlags.bInWheelieModeWheelsUp;
|
|
|
|
// Stunt jump races, we get erroneous collisions when driving on curved surfaces, so suppress them
|
|
if(g_ReflectionsAudioEntity.IsStuntTunnelMaterial(GetParent()->GetMainWheelMaterial()) || m_Parent->GetVehicleModelNameHash() == ATSTRINGHASH("OPPRESSOR", 0x34B82784))
|
|
{
|
|
if(now - m_JumpStartTime > g_StuntJumpLandingMinJumpTimeMs)
|
|
{
|
|
TriggerJumpLand(m_JumpLandMag, justFinishedWheelie, useBigJump);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TriggerJumpLand(m_JumpLandMag, justFinishedWheelie, useBigJump);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(numWheelsWereOnGround - numWheelsOnGround > 0)
|
|
{
|
|
m_JumpStartTime = now;
|
|
}
|
|
|
|
if(impactVelocity > g_TrailerBumpVel && now > (m_LastTrailerBumpTime + 1000))
|
|
{
|
|
audTrailerAudioEntity* trailer = GetParent()->GetTrailer();
|
|
|
|
if(trailer && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
trailer->TriggerTrailerBump(damageParams.Volume);
|
|
m_LastTrailerBumpTime = now;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_ImpactDamage = 0.f;
|
|
m_JumpLandMag = 0.f;
|
|
|
|
m_CollisionEventsList.ResetToReuseList();
|
|
}
|
|
|
|
void audVehicleCollisionAudio::UpdateDeformation(const float damage, const Vector3 & UNUSED_PARAM(offset) , CEntity * inflictor)
|
|
{
|
|
if(inflictor && inflictor->GetIsTypeVehicle())
|
|
{
|
|
//TODO entity specific checks e.g. for vehicles containing other vehicles
|
|
}
|
|
m_ImpactDamage += damage;
|
|
}
|
|
|
|
void audVehicleCollisionAudio::ProcessFoliage()
|
|
{
|
|
if(GetParent()->GetCachedVelocity().Mag2() < g_VehicleSpeedSqForFoliage)
|
|
{
|
|
m_Foliage.numCollisions = 0;
|
|
}
|
|
if(m_Foliage.numCollisions && (m_Parent->IsReal() || m_Parent->IsDummy()))
|
|
{
|
|
audSoundInitParams foliageParams;
|
|
foliageParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
foliageParams.TrackEntityPosition = true;
|
|
foliageParams.u32ClientVar = AUD_VEHICLE_SOUND_FOLIAGE;
|
|
foliageParams.UpdateEntity = true;
|
|
|
|
bool playLoop = false;
|
|
if(!m_FoliageLoop)
|
|
{
|
|
playLoop = true;
|
|
GetParent()->CreateSound_PersistentReference(sm_SoundSet.Find(ATSTRINGHASH("foliage_loop", 0xB2EBF067)), &m_FoliageLoop, &foliageParams);
|
|
}
|
|
|
|
if(m_FoliageLoop)
|
|
{
|
|
if(playLoop)
|
|
{
|
|
m_FoliageLoop->PrepareAndPlay();
|
|
}
|
|
m_PlayingFoliageThisFrame = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(m_FoliageLoop)
|
|
{
|
|
m_FoliageLoop->StopAndForget();
|
|
}
|
|
}
|
|
|
|
m_Foliage.drag = 0.f;
|
|
m_Foliage.numCollisions = 0;
|
|
}
|
|
|
|
void audVehicleCollisionAudio::TriggerScrapeImpact(audSoundInitParams & initParams)
|
|
{
|
|
VehicleCollisionSettings *settings = GetVehicleCollisionSettings();
|
|
|
|
if(Water::IsCameraUnderwater())
|
|
{
|
|
if(GetVehicle()->GetVehicleType() == VEHICLE_TYPE_PLANE)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if(g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
f32 magVel = GetVehicle()->GetVelocity().Mag();
|
|
|
|
initParams.Volume = m_ScrapeImpactCurve.CalculateRescaledValue(0.f, 1.f, settings->ScrapeMin, settings->ScrapeMax, magVel);
|
|
|
|
GetParent()->CreateAndPlaySound(settings->SmallScrapeImpact, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(settings->SmallScrapeImpact, &initParams, GetParent()->GetOwningEntity()));
|
|
|
|
initParams.Volume = m_SlowScrapeImpactCurve.CalculateRescaledValue(0.f, 1.f, settings->ScrapeMin, settings->ScrapeMax, magVel);
|
|
|
|
GetParent()->CreateAndPlaySound(settings->SlowScrapeImpact, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(settings->SlowScrapeImpact, &initParams, GetParent()->GetOwningEntity()));
|
|
|
|
}
|
|
}
|
|
|
|
void audVehicleCollisionAudio::TriggerJumpLand(const f32 impulseMag, bool justFinishedWheelie, bool useBigJumpLandSound)
|
|
{
|
|
const u32 timeInMs = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
const f32 impulseMagScalar = (GetVehicle()->GetStatus() == STATUS_WRECKED ? 2.f : 1.f);
|
|
|
|
if(g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) - GetParent()->GetLastTimeExploded() < g_VehicleExplosionAudioBoostTime)
|
|
{
|
|
Fakeimpact(GetParent()->GetVehicle()->GetVelocity().Mag(), impulseMagScalar, true);
|
|
}
|
|
|
|
if(impulseMag*impulseMagScalar > g_VehCollisionJumpLandMag)
|
|
{
|
|
if(timeInMs > m_LastJumpLandTime + g_jumpLandTimeFilter)
|
|
{
|
|
audSoundInitParams initParams;
|
|
initParams.UpdateEntity = true;
|
|
initParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
initParams.Tracker = GetVehicle()->GetPlaceableTracker();
|
|
initParams.SetVariableValue(ATSTRINGHASH("impulse", 0xB05D6C92), impulseMag);
|
|
f32 volume = g_SilenceVolume;
|
|
|
|
audCurve curve;
|
|
VehicleCollisionSettings * colSettings = GetVehicleCollisionSettings();
|
|
if(curve.Init((colSettings->JumpLandVolCurve)))
|
|
{
|
|
f32 volumeLinear = curve.CalculateValue(impulseMag);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
|
|
if(volume > g_SilenceVolume)
|
|
{
|
|
const f32 suspensionHealth = m_Parent->ComputeEffectiveSuspensionHealth();
|
|
bool isInteriorView = audNorthAudioEngine::IsRenderingFirstPersonVehicleCam() && GetParent()->IsFocusVehicle();
|
|
initParams.Volume = volume;
|
|
initParams.SetVariableValue(ATSTRINGHASH("SUSPENSION_HEALTH", 0x66758432), suspensionHealth);
|
|
|
|
#if __BANK
|
|
if(m_Parent->IsFocusVehicle() && g_DebugHydraulicSounds)
|
|
{
|
|
audDisplayf("Triggering jump landing with suspension health %.02f", suspensionHealth);
|
|
}
|
|
#endif
|
|
|
|
f32 bodyDamageFactor = Max(m_Parent->GetScriptBodyDamageFactor(), 1.0f - (GetVehicle()->GetVehicleDamage()->GetBodyHealth() / CVehicleDamage::GetBodyHealthMax()));
|
|
|
|
if (m_Parent->IsToyCar() && !useBigJumpLandSound)
|
|
{
|
|
audSoundSet soundSet;
|
|
|
|
if (soundSet.Init(ATSTRINGHASH("rcbandito_sounds", 0x7B9D9FE0)))
|
|
{
|
|
const u32 soundName = ATSTRINGHASH("small_jump_land", 0xFAB667EB);
|
|
const audMetadataRef jumpLandingSound = soundSet.Find(soundName);
|
|
|
|
if (jumpLandingSound != g_NullSoundRef)
|
|
{
|
|
GetParent()->CreateAndPlaySound(jumpLandingSound, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(soundSet.GetNameHash(), soundName, &initParams, GetParent()->GetOwningEntity(), GetVehicle()));
|
|
}
|
|
}
|
|
}
|
|
else if(m_Parent->GetVehicle()->InheritsFromAutomobile() && static_cast<CAutomobile*>(m_Parent->GetVehicle())->HasHydraulicSuspension())
|
|
{
|
|
audSoundSet hydraulicSuspensionSoundSet;
|
|
hydraulicSuspensionSoundSet.Init(m_Parent->GetVehicleHydraulicsSoundSetName());
|
|
|
|
if(hydraulicSuspensionSoundSet.IsInitialised())
|
|
{
|
|
u32 nameHash = g_NullSoundHash;
|
|
u32 timeSinceHydraulicsModified = fwTimer::GetTimeInMilliseconds() - m_Parent->GetLastHydraulicEngageDisengageTime();
|
|
|
|
// Suppress landing sounds for a short period after activating/deactivating the hydraulics
|
|
if(timeSinceHydraulicsModified > g_HydraulicsJumpLandSuppressionTime)
|
|
{
|
|
if(m_Parent->HydraulicsModifiedRecently())
|
|
{
|
|
nameHash = ATSTRINGHASH("Hydraulic_Suspension_Landing", 0x6E130876);
|
|
}
|
|
else
|
|
{
|
|
if(bodyDamageFactor > g_BodyDamageFactorForHydraulicDebris && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
nameHash = ATSTRINGHASH("Hydraulic_Suspension_Landing_Loose", 0x11A52128);
|
|
}
|
|
else
|
|
{
|
|
nameHash = ATSTRINGHASH("Hydraulic_Suspension_Landing_Hard", 0xAFFDD401);
|
|
}
|
|
}
|
|
}
|
|
|
|
const audMetadataRef jumpLandingSound = hydraulicSuspensionSoundSet.Find(nameHash);
|
|
|
|
if(jumpLandingSound != g_NullSoundRef)
|
|
{
|
|
GetParent()->CreateAndPlaySound(jumpLandingSound, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(hydraulicSuspensionSoundSet.GetNameHash(), nameHash, &initParams, GetParent()->GetOwningEntity(), GetVehicle()));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
audSoundSet wheelieSoundSet;
|
|
|
|
if (justFinishedWheelie && wheelieSoundSet.Init(ATSTRINGHASH("mod_vehicle_wheely_SS", 0x860473AA)))
|
|
{
|
|
const u32 soundName = ATSTRINGHASH("Wheely_Suspension_Land", 0xE7D5E94E);
|
|
GetParent()->CreateAndPlaySound(wheelieSoundSet.Find(soundName), &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(wheelieSoundSet.GetNameHash(), soundName, &initParams, GetParent()->GetOwningEntity(), GetVehicle()));
|
|
}
|
|
else
|
|
{
|
|
if (g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
GetParent()->CreateAndPlaySound(GetParent()->GetJumpLandingSound(isInteriorView), &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(GetParent()->GetJumpLandingSound(false), &initParams, GetParent()->GetOwningEntity(), GetVehicle()));
|
|
}
|
|
|
|
if (bodyDamageFactor > g_BodyDamageFactorForDebris && g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
GetParent()->CreateAndPlaySound(GetParent()->GetDamagedJumpLandingSound(isInteriorView), &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(GetParent()->GetDamagedJumpLandingSound(false), &initParams, GetParent()->GetOwningEntity(), GetVehicle()));
|
|
}
|
|
|
|
if (m_Parent->GetVehicleModelNameHash() == ATSTRINGHASH("MINITANK", 0xB53C6C52) && m_Parent->IsWaitingOnActivatedJumpLand())
|
|
{
|
|
audSoundSet jumpLandSoundSet;
|
|
|
|
if (jumpLandSoundSet.Init(m_Parent->IsFocusVehicle() ? ATSTRINGHASH("ch_vehicle_minitank_player_sounds", 0xCF2B0226) : ATSTRINGHASH("ch_vehicle_minitank_remote_sounds", 0x90AF5388)))
|
|
{
|
|
GetParent()->CreateAndPlaySound(jumpLandSoundSet.Find(ATSTRINGHASH("jump_land", 0x6B2460BA)), &initParams);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_LastJumpLandTime = timeInMs;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_Parent->ResetWaitingOnActivatedJumpLand();
|
|
}
|
|
|
|
void audVehicleCollisionAudio::TriggerJumpLandScrape()
|
|
{
|
|
const u32 timeInMs = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
if(timeInMs > m_LastJumpLandScrapeTime + g_jumpLandScrapeTimeFilter)
|
|
{
|
|
VfxGroup_e vfxGroup = PGTAMATERIALMGR->GetMtlVfxGroup(m_Parent->GetMainWheelMaterial());
|
|
VfxWheelInfo_s* pVfxWheelInfo = g_vfxWheel.GetInfo(VFXTYRESTATE_BURST_DRY, vfxGroup);
|
|
|
|
if(pVfxWheelInfo)
|
|
{
|
|
CVfxVehicleInfo* pVfxVehicleInfo = g_vfxVehicleInfoMgr.GetInfo(m_Parent->GetVehicle());
|
|
|
|
if(pVfxVehicleInfo)
|
|
{
|
|
atHashWithStringNotFinal ptFxHashName;
|
|
|
|
if (pVfxVehicleInfo->GetWheelGenericPtFxSet()==2)
|
|
{
|
|
ptFxHashName = pVfxWheelInfo->ptFxWheelFric2HashName;
|
|
}
|
|
else
|
|
{
|
|
ptFxHashName = pVfxWheelInfo->ptFxWheelFric1HashName;
|
|
}
|
|
|
|
if(ptFxHashName.GetHash() == ATSTRINGHASH("rim_fric_hard", 0x9F6F204C))
|
|
{
|
|
audSoundInitParams initParams;
|
|
f32 speedRatio = Clamp((m_Parent->GetCachedVelocity().Mag() - 10.0f)/30.0f, 0.0f, 1.0f);
|
|
initParams.Volume = audDriverUtil::ComputeDbVolumeFromLinear(speedRatio);
|
|
|
|
if(initParams.Volume > g_SilenceVolume)
|
|
{
|
|
initParams.UpdateEntity = true;
|
|
initParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
initParams.Tracker = GetVehicle()->GetPlaceableTracker();
|
|
m_LastJumpLandScrapeTime = timeInMs;
|
|
|
|
GetParent()->CreateAndPlaySound(ATSTRINGHASH("JUMP_LAND_SCRAPE", 0xCCEC4586), &initParams);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void audVehicleCollisionAudio::PostUpdate()
|
|
{
|
|
u32 now = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
if(m_WasHitByTrain)
|
|
{
|
|
audSoundInitParams initParams;
|
|
initParams.UpdateEntity = true;
|
|
initParams.EnvironmentGroup = GetParent()->GetEnvironmentGroup();
|
|
initParams.TrackEntityPosition = true;
|
|
|
|
VehicleCollisionSettings * settings = GetVehicleCollisionSettings();
|
|
|
|
if(settings)
|
|
{
|
|
if((g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) - m_LastTimeHitByTrain) > sm_TrainLoopTime)
|
|
{
|
|
GetParent()->CreateAndPlaySound(settings->TrainImpact, &initParams);
|
|
}
|
|
else
|
|
{
|
|
if(!m_TrainLoop)
|
|
{
|
|
GetParent()->CreateAndPlaySound_Persistent(settings->TrainImpactLoop, &m_TrainLoop, &initParams);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_LastTimeHitByTrain = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
m_WasHitByTrain = false;
|
|
}
|
|
else
|
|
{
|
|
if(m_TrainLoop)
|
|
{
|
|
m_TrainLoop->StopAndForget();
|
|
}
|
|
}
|
|
|
|
if(m_CarRollSound)
|
|
{
|
|
if(now > m_LastCarRollTime + g_VehicleRollHoldTime)
|
|
{
|
|
m_CarRollSound->StopAndForget();
|
|
}
|
|
}
|
|
|
|
ProcessVehicleCollision();
|
|
ProcessFakeImpact();
|
|
|
|
m_FakeImpactMagThisFrame = 0.f;
|
|
m_VehicleCollisionContext.Init();
|
|
m_VehicleCollisionContext.collisionEvent.Init();
|
|
m_NeedsToPlayVehOnVehImpact = false;
|
|
m_ImpactThisFrame = false;
|
|
m_IsOnAnotherVehicle = false;
|
|
}
|
|
|
|
void audVehicleCollisionAudio::WreckVehicle()
|
|
{
|
|
m_WreckedTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
}
|
|
|
|
void audVehicleCollisionAudio::HandleMelee()
|
|
{
|
|
m_LastMeleeTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
}
|
|
|
|
|
|
#if __BANK
|
|
void audVehicleCollisionAudio::AddWidgets(bkBank &bank)
|
|
{
|
|
bank.PushGroup("Collision Audio");
|
|
bank.AddSlider("Land impact mag", &g_VehCollisionJumpLandMag, 0.f, 10.f, 0.1f);
|
|
bank.AddSlider("Jump land time filter", &g_jumpLandTimeFilter, 0, 1000, 10);
|
|
bank.AddSlider("Jump land scrape time filter", &g_jumpLandScrapeTimeFilter, 0, 1000, 10);
|
|
bank.AddSlider("RC Car Min Time for Big Jump Landing", &g_ToyCarMinAirTimeForBigLanding, 0, 10000, 10);
|
|
bank.AddSlider("RC Car Min Big Jump Land Duration", &g_ToyCarMinBigJumpLandDuration, 0, 10000, 10);
|
|
bank.AddSlider("RC Car Min Small Jump Land Duration", &g_ToyCarMinSmallJumpLandDuration, 0, 10000, 10);
|
|
bank.AddToggle("Debug vehicle collision audio", &g_DebugVehicleCollisionAudio);
|
|
bank.AddSlider("Explosion Collision Volume Boost", &g_VehicleExplosionCollisionAudioVolumeBoost, -100.f, 100.f, 1.f);
|
|
bank.AddSlider("Explosion Collision Rolloff Boost", &g_VehicleExplosionCollisionAudioRolloffBoost, 0.f, 100.f, 0.1f);
|
|
bank.AddSlider("Explosion boost time", &g_VehicleExplosionAudioBoostTime, 0, 100000, 10);
|
|
bank.AddSlider("Boat bottom collision threshold", &g_BoatBottomContactDotThreshold, 0.f, 1.f, 0.01f);
|
|
bank.AddToggle("Apply Boat Body Collision Limiting", &g_ApplyBoatBodyCollisionLimitingToAllBoats);
|
|
bank.AddSlider("g_VehicleSpeedSqForFoliage", &g_VehicleSpeedSqForFoliage, 0.f, 10.f, 0.1f);
|
|
bank.AddSlider("g_BlockingTimeForFakeImpacts", &g_BlockingTimeForFakeImpacts, 0, 1000, 10);
|
|
bank.AddSlider("g_BlockingTimeForVehicleCollisions", &g_BlockingTimeForVehicleCollisions, 0, 1000, 10);
|
|
bank.AddToggle("g_NoVehicleCollisions", &g_NoVehicleCollisions);
|
|
bank.AddToggle("g_NoFakeVehicleCollisions", &g_NoFakeVehicleCollisions);
|
|
bank.AddToggle("g_AlwaysPlayFakeCollisions", &g_AlwaysPlayFakeCollisions);
|
|
bank.AddToggle("g_NoDeformationAudio", &g_NoDeformationAudio);
|
|
bank.AddToggle("g_NoCrashSweetener", &g_NoCrashSweetener);
|
|
bank.AddToggle("g_NoHeadlightSmashAudio", &g_NoHeadlightSmashAudio);
|
|
bank.AddToggle("g_NoVehicleScrapeAudio", &g_NoVehicleScrapeAudio);
|
|
bank.AddSlider("g_VehicleRollHoldTime", &g_VehicleRollHoldTime, 0, 10000, 100);
|
|
bank.AddSlider("sm_MinSpeedForVehicleCollisions", &sm_MinSpeedForVehicleCollisions, 0.f, 10.f, 0.1f);
|
|
bank.AddSlider("sm_TimeForBikePostMeleeImpacts", &sm_TimeForBikePostMeleeImpacts, 0, 10000, 100);
|
|
bank.PopGroup();
|
|
}
|
|
|
|
void audVehicleCollisionAudio::UpdateCollisionSettings()
|
|
{
|
|
sm_UpdateVehicleCollisionSettings = true;
|
|
}
|
|
#endif
|