Files
GTASource/game/event/EventSound.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

543 lines
15 KiB
C++

//
// event/EventSound.cpp
//
// Copyright (C) 1999-2011 Rockstar Games. All Rights Reserved.
//
#include "event/EventSound.h"
#include "audio/pedaudioentity.h" // Temp, audFootstepEventTuning is probably misplaced in 'EventSound.psc'.
#include "Peds/ped.h"
#include "Peds/PedIntelligence.h"
#include "task/Combat/TaskReact.h"
#include "task/Combat/TaskThreatResponse.h"
#include "event/EventSound_parser.h"
AI_OPTIMISATIONS()
//-----------------------------------------------------------------------------
// CEventSoundBase
CEventSoundBase::CEventSoundBase(CEntity* pEntityMakingSound, float soundRange)
: m_pEntityMakingSound(pEntityMakingSound)
, m_SoundRange(soundRange)
{
// m_audioPathRequest = PATH_HANDLE_NULL; //static_cast<TPathHandle>(PATH_INVALID_HANDLE);
//
// if(-1==m_iTimeHeard)
// {
// Assert(m_pEntityMakingSound);
// m_iTimeHeard=fwTimer::GetTimeInMilliseconds();
// m_vTargetPosAtHeardTime=VEC3V_TO_VECTOR3(m_pEntityMakingSound->GetTransform().GetPosition());
// }
}
CEventSoundBase::~CEventSoundBase()
{
// Cancel any outstanding audio pathfinding requests
// if( m_audioPathRequest != PATH_HANDLE_NULL)
// {
// CPathServer::CancelRequest( m_audioPathRequest );
// m_audioPathRequest = PATH_HANDLE_NULL; //static_cast<TPathHandle>(PATH_INVALID_HANDLE);
// }
}
#if 0
//-------------------------------------------------------------------------
// Returns true if the event is ready, this is where any event processing
// is carried out, for example path finding requests
//-------------------------------------------------------------------------
bool CEventSoundBase::IsEventReady(CPed* /*pPed*/)
{
// Audio paths not yet implemented
/*
// If the audio request is invalid, begin an audio search
if( m_audioPathRequest == PATH_HANDLE_NULL )
{
if( GetSourceEntity() )
{
m_audioPathRequest = CPathServer::RequestAudioPath( GetSourceEntity()->GetPosition(), pPed->GetPosition(), m_Volume, NULL);
}
else
{
m_audioPathRequest = CPathServer::RequestAudioPath( m_vTargetPosAtHeardTime, pPed->GetPosition(), m_Volume, NULL);
}
}
else
{
// There is a request outstanding, is it finised?
if( CPathServer::IsRequestResultReady( m_audioPathRequest )==ERequest_Ready )
{
// [TODO] PH query the audio path result, not implemented on the pathserver side yet
CPathServer::CancelRequest( m_audioPathRequest );
m_audioPathRequest = PATH_HANDLE_NULL;
m_VolumeFromPathServer = GetSoundLevel( NULL, pPed->GetPosition() );
// reset any already calculated task,
// and re-calculate
m_iTaskType = CTaskTypes::TASK_NONE;
ComputeResponseTaskType( pPed, false );
return true;
}
}
return false;
*/
}
#endif // 0
/*
PURPOSE
Returns true if the sound event will affect the ped
for a dynamic event
*/
bool CEventSoundBase::AffectsPedCore(CPed* pPed) const
{
// Check to make sure the listening ped can respond to sound events.
if(!pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_ListensToSoundEvents))
return false;
if(pPed->IsDead())
return false;
if(!GetSourceEntity())
return false;
// Check to make sure the source ped (if there is one) can generate sound events.
CPed* pSourcePed = GetSourcePed();
if (pSourcePed && !pSourcePed->GetPedConfigFlag(CPED_CONFIG_FLAG_GeneratesSoundEvents))
{
return false;
}
// In the best case scenario, a direct LOS, would the ped be able to hear this
// event? if not don't add the event, there's no point calculating the audio path
// as theres no-way they would be able to hear
if(!CanPedHearSound(*pPed, m_pEntityMakingSound->GetTransform().GetPosition(), m_SoundRange))
{
return false;
}
return true;
}
bool CEventSoundBase::ShouldCreateResponseTaskForPed(const CPed* pPed) const
{
// reject this event if the source entity is no longer valid, or if it was this ped that generated the event
if( ( 0==GetSourceEntity() ) || ( GetSourceEntity() == pPed ) )
{
return false;
}
return true;
}
bool CEventSoundBase::operator==(const fwEvent& otherEvent) const
{
if(static_cast<const CEvent&>(otherEvent).GetEventType() == GetEventType())
{
const CEventSoundBase& otherSoundEvent = static_cast<const CEventSoundBase&>(otherEvent);
if(otherSoundEvent.m_pEntityMakingSound.Get() == m_pEntityMakingSound.Get() &&
otherSoundEvent.m_SoundRange == m_SoundRange &&
otherSoundEvent.m_iTaskType == m_iTaskType &&
otherSoundEvent.m_FleeFlags == m_FleeFlags &&
otherSoundEvent.m_SpeechHash == m_SpeechHash &&
otherSoundEvent.m_bWitnessedFirstHand == m_bWitnessedFirstHand)
{
return true;
}
}
return false;
}
bool CEventSoundBase::CanPedHearSound(const CPed& listener, Vec3V_In soundPos, const float& soundRange)
{
const Vec3V soundPosV = soundPos;
const Vec3V listenerPosV = listener.GetTransform().GetPosition();
const ScalarV soundRangeV = LoadScalar32IntoScalarV(soundRange);
const ScalarV soundRangeSqV = Scale(soundRangeV, soundRangeV);
const ScalarV distSqV = DistSquared(soundPosV, listenerPosV);
return IsLessThanAll(distSqV, soundRangeSqV) != 0;
// For reference, this is how the hearing logic used to work for CEventFootStepHeard:
#if 0
// Only accept noise that the ped can hear
Vector3 vDiff = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition() - m_pNoiseMaker->GetTransform().GetPosition());
float fDist2 = vDiff.Mag2();
if (fDist2 > (fHearingRange * fHearingRange))
{
// Footstep was out of range
return false;
}
else if (fDist2 > 1.0f) // Avoid divide by zero
{
// Scale to range 0.0f, 1.0f
float fLoudnessScaled = m_fLoudness / ms_fMaxLoudness;
// Footstep was in hearing range, scale the loudness depending on how far away the ped is
fLoudnessScaled *= fHearingRange / Sqrtf(fDist2);
// TODO: Make this detection criteria better
// The bigger this number the louder the sound is / the closer we are
// E.g if we take the player's footsteps loudness = 7.5
// if the ped has a hearing range of 60.0f and is 5m away
// fLoudnessScaled = 0.075*60 / 5 = 0.9
// fLoudnessScaled = 0.075*60 / 10 = 0.45
if (fLoudnessScaled < 0.7f)
{
return false;
}
}
else
{
// distance must be < 1m
}
#endif
}
//-----------------------------------------------------------------------------
// CEventCommunicateEvent
/*
PURPOSE
Constructor.
*/
CEventCommunicateEvent::CEventCommunicateEvent(CEntity* pEntityMakingSound, CEvent* pEvent, float soundRange)
//, const int iTimeHeard, const Vector3& vTargetPosAtHeardTime )
: m_bFinished(false)
{
#if !__FINAL
m_EventType = EVENT_COMMUNICATE_EVENT;
#endif
m_pEntityMakingSound = pEntityMakingSound;
m_SoundRange = soundRange;
Assert( pEvent );
m_pEvent = pEvent;
// m_audioPathRequest = 0;
// if(-1==m_iTimeHeard && m_pEntityMakingSound)
// {
// m_iTimeHeard=fwTimer::GetTimeInMilliseconds();
// m_vTargetPosAtHeardTime= VEC3V_TO_VECTOR3(m_pEntityMakingSound->GetTransform().GetPosition());
// }
}
/*
PURPOSE
Destructor.
*/
CEventCommunicateEvent::~CEventCommunicateEvent()
{
// Cancel any outstanding audio pathfinding requests
// if( m_audioPathRequest != PATH_HANDLE_NULL )
// {
// CPathServer::CancelRequest( m_audioPathRequest );
// m_audioPathRequest = PATH_HANDLE_NULL;
// }
if( m_pEvent )
{
delete m_pEvent;
m_pEvent = NULL;
}
}
CEvent* CEventCommunicateEvent::Clone() const
{
return rage_new CEventCommunicateEvent( m_pEntityMakingSound, static_cast<CEvent*>(m_pEvent->Clone()), m_SoundRange); //, m_iTimeHeard, m_vTargetPosAtHeardTime );
}
/*
PURPOSE
Returns true if the event is ready, this is where any event processing
is carried out, for example path finding requests.
*/
bool CEventCommunicateEvent::IsEventReady(CPed* pPed)
{
if(CanBeHeardBy(pPed))
{
Assert(CEvent::GetPool()->IsValidPtr(m_pEvent));
pPed->GetPedIntelligence()->AddEvent( *m_pEvent, false, false );
m_bFinished = true;
}
return true;
}
/*
PURPOSE
Returns true if the communication event will be able to reach the ped.
*/
bool CEventCommunicateEvent::AffectsPedCore(CPed* pPed) const
{
if (!CanBeCommunicatedTo(pPed))
{
return false;
}
if( !m_pEvent->AffectsPed( pPed ) )
{
return false;
}
return true;
}
/*
PURPOSE
Returns true if the communication event can be communicated to the ped (without taking into account it the communicated event itself can affect him).
*/
bool CEventCommunicateEvent::CanBeCommunicatedTo(const CPed* pPed) const
{
if (!pPed)
{
return false;
}
if( m_bFinished )
{
return false;
}
if(pPed->IsPlayer())
return false;
if(pPed->IsDead())
return false;
if(!GetSourceEntity())
return false;
if( !m_pEvent )
{
return false;
}
// In the best case scenario, a direct LOS, would the ped be able to hear this
// event? if not don't add the event, there's no point calculating the audio path
// as theres no-way they would be able to hear
if(!CanBeHeardBy(pPed))
{
return false;
}
return true;
}
/*
PURPOSE
Returns true if the communication event is at hear range.
*/
bool CEventCommunicateEvent::CanBeHeardBy(const CPed* pPed) const
{
if (!pPed)
{
return false;
}
const CEntity* pSrc = GetSourceEntity();
if (!pSrc)
{
return false;
}
const float fSoundRange = pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_UseMaxSenseRangeWhenReceivingEvents) ?
pPed->GetPedIntelligence()->GetPedPerception().GetSenseRange() : m_SoundRange;
return CEventSoundBase::CanPedHearSound(*pPed, pSrc->GetTransform().GetPosition(), fSoundRange);
}
bool CEventCommunicateEvent::operator==(const fwEvent& otherEvent) const
{
if(static_cast<const CEvent&>(otherEvent).GetEventType() == GetEventType())
{
const CEventCommunicateEvent& otherCommunicateEvent = static_cast<const CEventCommunicateEvent&>(otherEvent);
if(m_pEntityMakingSound == otherCommunicateEvent.m_pEntityMakingSound &&
m_SoundRange == otherCommunicateEvent.m_SoundRange &&
m_bFinished == otherCommunicateEvent.m_bFinished)
{
if(m_pEvent != NULL && otherCommunicateEvent.m_pEvent != NULL && *m_pEvent == *otherCommunicateEvent.m_pEvent)
{
return true;
}
else if(m_pEvent == otherCommunicateEvent.m_pEvent)
{
return true;
}
}
}
return false;
}
//-----------------------------------------------------------------------------
// CEventFootStepHeard
CEventFootStepHeard::Tunables CEventFootStepHeard::sm_Tunables;
IMPLEMENT_SOUND_EVENT_TUNABLES(CEventFootStepHeard, 0x1d8409c6);
CEventFootStepHeard::CEventFootStepHeard(CPed* pPedHeardWalking, float soundRange)
: CEventSoundBase(pPedHeardWalking, soundRange)
{
#if !__FINAL
m_EventType = EVENT_FOOT_STEP_HEARD;
#endif
}
CEventEditableResponse* CEventFootStepHeard::CloneEditable() const
{
return rage_new CEventFootStepHeard(GetSourcePed(), m_SoundRange);
}
bool CEventFootStepHeard::CreateResponseTask(CPed* pPed, CEventResponse& response) const
{
// Not sure if this is necessary:
if (pPed->IsLocalPlayer())
{
return false;
}
// Make sure ped exists
CPed* pNoiseMaker = GetSourcePed();
if (pNoiseMaker)
{
response.SetEventResponse(CEventResponse::EventResponse, rage_new CTaskThreatResponse(pNoiseMaker));
}
return response.HasEventResponse();
}
bool CEventFootStepHeard::AffectsPedCore(CPed* pPed) const
{
if(!CEventSoundBase::AffectsPedCore(pPed))
{
return false;
}
// Not sure if this is necessary:
CPed* pNoiseMaker = GetSourcePed();
if(pPed->GetPedIntelligence()->IsFriendlyWith(*pNoiseMaker))
{
return false;
}
// Treat crouching peds or peds in stealth mode as unhearable
if(pNoiseMaker->GetIsCrouching() || pNoiseMaker->GetMotionData()->GetUsingStealth())
{
return false;
}
// If CPED_CONFIG_FLAG_CheckLoSForSoundEvents is set, don't use this event to respond to the ped; it'll be checked for in CPedAcquaintanceScanner [5/14/2013 mdawe]
if (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_CheckLoSForSoundEvents))
{
// Delegate to acquaintance scanner
if (CPedAcquaintanceScanner::ms_bFixCheckLoSForSoundEvents)
{
static const ScalarV scDEFAULT_MAX_HEARING_DISTANCE_WHEN_CHECKING_LOS(3.0f);
ScalarV scMaxHearingDistanceSquared(scDEFAULT_MAX_HEARING_DISTANCE_WHEN_CHECKING_LOS);
if (pPed->GetPedIntelligence()->GetCombatBehaviour().GetCombatFloat(kAttribFloatMaxDistanceToHearEventsUsingLOS) >= 0)
{
scMaxHearingDistanceSquared = ScalarVFromF32(rage::square(pPed->GetPedIntelligence()->GetCombatBehaviour().GetCombatFloat(kAttribFloatMaxDistanceToHearEventsUsingLOS)));
}
ScalarV scCurrentDistanceSq = DistSquared(pPed->GetTransform().GetPosition(), pNoiseMaker->GetTransform().GetPosition());
if (IsLessThanAll(scMaxHearingDistanceSquared, scCurrentDistanceSq))
{
return false;
}
pPed->GetPedIntelligence()->GetEventScanner()->GetAcquaintanceScanner().RegisterScanRequest(*pNoiseMaker, 0x0, TargetOption_UseFOVPerception);
}
return false;
}
// We're not checking LOS. Script can set a distance beyond which sounds should not be heard.
else if (pPed->GetPedIntelligence()->GetCombatBehaviour().GetCombatFloat(kAttribFloatMaxDistanceToHearEvents) >= 0)
{
ScalarV vMaxDistanceSq = ScalarVFromF32(rage::square(pPed->GetPedIntelligence()->GetCombatBehaviour().GetCombatFloat(kAttribFloatMaxDistanceToHearEvents)));
ScalarV vCurrentDistanceSq = DistSquared(pPed->GetTransform().GetPosition(), pNoiseMaker->GetTransform().GetPosition());
if (IsLessThanAll(vMaxDistanceSq, vCurrentDistanceSq))
{
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
// For reference, this is the old sound attenuation model used by CEvent.
// Even if we now keep track of the sound range in meters, we could convert
// to dB, apply this attenuation model, and compare against a sensitivity threshold.
// This could make sense if we need something more accurate, perhaps taking into
// account background noise and different sensitivity for different animals.
#if 0
float CEvent::GetSoundLevel(const CEntity* pSourceEnt, const Vector3& vecMicPos) const
{
if(pSourceEnt != NULL && GetSourceEntity() != pSourceEnt)
{
return 0.0f;
}
else if(GetLocalSoundLevel() == 0.0f)
{
return 0.0f;
}
float fRange = 1000.0f;
if(GetSourceEntity())
{
fRange = (vecMicPos - VEC3V_TO_VECTOR3(GetSourceEntity()->GetTransform().GetPosition())).Mag();
}
if(fRange < 1.0f)
{
fRange = 1.0f;
}
// Decimal power level divided by distance squared can be calculated
// in decibel scale by subtracting decibel scale range (kinda)
// since: Log(a/b) = Log(a) - Log(b)
// also take into account atmospheric absorption at a rate of 1db per 100m
return (GetLocalSoundLevel() - 10.0f * rage::Log10(fRange * fRange) - fRange / 100.0f);
}
float CEvent::CalcSoundLevelIncrement(float fOrig, float fAdd)
{
if(fAdd == 0.0f)
{
return 0.0f;
}
else if(fOrig == 0.0f)
{
return fAdd;
}
// Need to convert decibel levels to decimal power levels
fOrig = rage::Powf(10.0f, 0.1f * fOrig);
fAdd = rage::Powf(10.0f, 0.1f * fAdd);
// Then use ratio of new total to old total to work out how much decibels to add
fAdd = 10.0f * rage::Log10((fOrig + fAdd) / fOrig);
return fAdd;
}
#endif // 0
// End of file 'event/EventSound.cpp'