diff --git a/resource/dist/ModelExtras.ini b/resource/dist/ModelExtras.ini index 5f68a6ef..7c694cd6 100755 --- a/resource/dist/ModelExtras.ini +++ b/resource/dist/ModelExtras.ini @@ -67,11 +67,13 @@ TailLightShadowIntensity = 80 # Opacity of projected groun [SOUND] SoundEffects = 1 # Master toggle for all ModelExtras sound effects SoundMult = 0.6 # Multiplier for ModelExtras audio effects (0.0 to 1.0) -SoundEffects_GlobalAirbreakSound = 1 # Plays engine air brake sound for big vehicles (check [TABLE]) -SoundEffects_GlobalEngineSound = 1 # Plays engine on/off sound -SoundEffects_GlobalIndicatorSound = 1 # Plays indicator on/off sound -SoundEffects_GlobalReverseSound = 1 # Plays reverse sound for big vehicles (check [TABLE]) -SoundEffects_NonPlayerVehicles = 1 # Plays sound effects for traffic vehicles too +GlobalAirbreakSound = 1 # Plays engine air brake sound for big vehicles (check [TABLE]) +GlobalEngineSound = 1 # Plays engine on/off sound +GlobalIndicatorSound = 1 # Plays indicator on/off sound +GlobalReverseSound = 1 # Plays reverse sound for big vehicles (check [TABLE]) +DoorChimeSound = 1 # Plays open door warning chime sound for vehicles (check [TABLE]) +DoorChimeOnlySelected = 1 # Open door warning chime for selected vehicles only (check [TABLE]) +NonPlayerVehicles = 1 # Plays sound effects for traffic vehicles too [CONFIG] EnableLiveReload = 1 # Enables the 'MERELOAD' cheat to live-reload vehicle configs and INI settings without restarting @@ -93,5 +95,6 @@ SirenLightKey = 0x4C # Toggle emergency siren lig SpotLightKey = 0x42 # Toggle police searchlight / spotlight on / off (Default: B [0x42]) [TABLE] -SoundEffects_BigVehicleModels = "403, 406, 407, 408, 414, 416, 427, 428, 431, 433, 437, 443, 455, 456, 486, 498, 499, 508, 514, 515, 524, 525, 528, 530, 532, 544, 552, 573, 574, 578, 588, 601, 609" # Vehicle model IDs for air brake and reverse warning sounds +BigVehicleModels = "403, 406, 407, 408, 414, 416, 427, 428, 431, 433, 437, 443, 455, 456, 486, 498, 499, 508, 514, 515, 524, 525, 528, 530, 532, 544, 552, 573, 574, 578, 588, 601, 609" # Vehicle model IDs for air brake and reverse warning sounds BackFireEffect_VehicleModels = "402, 411, 415, 424, 429, 434, 439, 451, 461, 468, 475, 477, 480, 494, 496, 502, 503, 504, 506, 521, 522, 535, 541, 558, 559, 560, 561, 562, 565, 581, 587, 589, 603, 604" # Vehicle model IDs for sports exhaust backfire flames +DoorChime_VehicleModels = "400, 401, 402, 405, 409, 410, 411, 413, 415, 416, 418, 420, 421, 422, 426, 429, 436, 440, 445, 451, 458, 459, 470, 477, 480, 482, 489, 490, 492, 496, 500, 505, 506, 507, 516, 517, 526, 527, 529, 533, 540, 541, 546, 547, 550, 551, 554, 558, 559, 560, 561, 562, 565, 579, 582, 585, 587, 589, 596, 597, 598, 599, 602, 603" # Allowed vehicle model IDs for open door warning chime sound diff --git a/resource/dist/ModelExtras/audio/door_chime.mp3 b/resource/dist/ModelExtras/audio/door_chime.mp3 new file mode 100644 index 00000000..5ce7e1a0 Binary files /dev/null and b/resource/dist/ModelExtras/audio/door_chime.mp3 differ diff --git a/src/features/soundeffects.cpp b/src/features/soundeffects.cpp index e8042726..2fffa00d 100755 --- a/src/features/soundeffects.cpp +++ b/src/features/soundeffects.cpp @@ -4,9 +4,12 @@ #include "lights/lights.h" #include "eVehicleClass.h" +#include "utils/datamgr.h" + using namespace plugin; std::vector ValidForReverseSound; +static std::vector ValidForDoorChime; #define ANIMGROUP_TRUCK 2 #define ANIMGROUP_BUS 15 @@ -17,24 +20,80 @@ static bool bEngineSounds = false; static bool bIndicatorSounds = false; static bool bAirbreakSounds = false; static bool bOnlyPlayerVehicle = true; +static bool bDoorChimeSounds = true; +static bool bDoorChimeOnlySelected = true; +static float fDoorChimeVolume = 0.7f; + +static std::string GetDoorChimeAudioPath() +{ + const char *extensions[] = {".mp3", ".wav", ".ogg"}; + for (const char *ext : extensions) + { + std::string relPath = std::string("ModelExtras/audio/door_chime") + ext; + std::string fullPath = PLUGIN_PATH((char *)relPath.c_str()); + DWORD attr = GetFileAttributesA(fullPath.c_str()); + if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY)) + { + return fullPath; + } + } + return MOD_DATA_PATH("audio/door_chime.mp3"); +} + +static bool IsVehicleEligibleForDoorChime(int model) +{ + const auto &jsonData = DataMgr::Get(model); + if (jsonData.contains("sound") && jsonData["sound"].contains("door_chime")) + { + return jsonData["sound"]["door_chime"].get(); + } + + if (!bDoorChimeOnlySelected) + { + return true; + } + + if (!ValidForDoorChime.empty() && + std::find(ValidForDoorChime.begin(), ValidForDoorChime.end(), model) != ValidForDoorChime.end()) + { + return true; + } + + return false; +} void SoundEffects::ReloadConfig() { CBaseFeature::ReloadConfig(); - std::string line = gConfig.ReadString("TABLE", "SoundEffects_BigVehicleModels", ""); + std::string line = gConfig.ReadString("TABLE", "BigVehicleModels", ""); ValidForReverseSound.clear(); Util::GetModelsFromIni(line, ValidForReverseSound); - bReverseSounds = gConfig.ReadBoolean("SOUND", "SoundEffects_GlobalReverseSound", gConfig.ReadBoolean("FEATURES", "SoundEffects_GlobalReverseSound", false)); - bEngineSounds = gConfig.ReadBoolean("SOUND", "SoundEffects_GlobalEngineSound", gConfig.ReadBoolean("FEATURES", "SoundEffects_GlobalEngineSound", false)); - bIndicatorSounds = gConfig.ReadBoolean("SOUND", "SoundEffects_GlobalIndicatorSound", gConfig.ReadBoolean("FEATURES", "SoundEffects_GlobalIndicatorSound", false)); - bAirbreakSounds = gConfig.ReadBoolean("SOUND", "SoundEffects_GlobalAirbreakSound", gConfig.ReadBoolean("FEATURES", "SoundEffects_GlobalAirbreakSound", false)); - bOnlyPlayerVehicle = !gConfig.ReadBoolean("SOUND", "SoundEffects_NonPlayerVehicles", gConfig.ReadBoolean("FEATURES", "SoundEffects_NonPlayerVehicles", false)); + std::string doorChimeModelsLine = gConfig.ReadString("TABLE", "DoorChime_VehicleModels", "400, 401, 402, 405, 409, 410, 411, 413, 415, 416, 418, 420, 421, 422, 426, 429, 436, 440, 445, 451, 458, 459, 470, 477, 480, 482, 489, 490, 492, 496, 500, 505, 506, 507, 516, 517, 526, 527, 529, 533, 540, 541, 546, 547, 550, 551, 554, 558, 559, 560, 561, 562, 565, 579, 582, 585, 587, 589, 596, 597, 598, 599, 602, 603"); + ValidForDoorChime.clear(); + Util::GetModelsFromIni(doorChimeModelsLine, ValidForDoorChime); + + bReverseSounds = gConfig.ReadBoolean("SOUND", "GlobalReverseSound", false); + bEngineSounds = gConfig.ReadBoolean("SOUND", "GlobalEngineSound", false); + bIndicatorSounds = gConfig.ReadBoolean("SOUND", "GlobalIndicatorSound", false); + bAirbreakSounds = gConfig.ReadBoolean("SOUND", "GlobalAirbreakSound", false); + bOnlyPlayerVehicle = !gConfig.ReadBoolean("SOUND", "NonPlayerVehicles", false); + bDoorChimeSounds = gConfig.ReadBoolean("SOUND", "DoorChimeSound", true); + bDoorChimeOnlySelected = gConfig.ReadBoolean("SOUND", "DoorChimeOnlySelected", true); + fDoorChimeVolume = std::clamp(gConfig.ReadFloat("SOUND", "DoorChimeVolume", 0.7f), 0.0f, 1.0f); } void SoundEffects::Reload(CVehicle *pVeh) { ReloadConfig(); + if (pVeh) + { + auto &data = m_VehData.Get(pVeh); + if (data.m_hDoorChimeStream) + { + AudioMgr::StopLoopStream(data.m_hDoorChimeStream); + } + } } void SoundEffects::Init() @@ -175,4 +234,47 @@ void SoundEffects::ProcessVehicle(CVehicle *pVeh) } } + if (bDoorChimeSounds && pVeh->m_nVehicleSubClass == VEHICLE_AUTOMOBILE) + { + CAutomobile *pAuto = static_cast(pVeh); + bool isEligible = IsVehicleEligibleForDoorChime(model); + bool isFrontDoorOpen = false; + + if (isEligible && !pVeh->bEngineBroken && !pVeh->bIsDrowning) + { + bool leftOpen = (!pAuto->m_doors[eDoors::DOOR_FRONT_LEFT].IsClosed() && + std::abs(pAuto->m_doors[eDoors::DOOR_FRONT_LEFT].m_fAngle) > 0.15f); + bool rightOpen = (!pAuto->m_doors[eDoors::DOOR_FRONT_RIGHT].IsClosed() && + std::abs(pAuto->m_doors[eDoors::DOOR_FRONT_RIGHT].m_fAngle) > 0.15f); + + if (leftOpen || rightOpen) + { + if (isPlayerDriver || pVeh->bEngineOn || (pPlayer && MathUtil::DistanceSquared(pVeh->GetPosition(), pPlayer->GetPosition()) < (12.0f * 12.0f))) + { + isFrontDoorOpen = true; + } + } + } + + if (isFrontDoorOpen) + { + static std::string chimePath = GetDoorChimeAudioPath(); + if (!data.m_hDoorChimeStream) + { + data.m_hDoorChimeStream = AudioMgr::PlayLoopStream(chimePath, pVeh->GetPosition(), fDoorChimeVolume, 25.0f); + } + else + { + AudioMgr::UpdateLoopStream(data.m_hDoorChimeStream, pVeh->GetPosition(), fDoorChimeVolume, 25.0f); + } + } + else + { + if (data.m_hDoorChimeStream) + { + AudioMgr::StopLoopStream(data.m_hDoorChimeStream); + } + } + } + } \ No newline at end of file diff --git a/src/features/soundeffects.h b/src/features/soundeffects.h index 5067db3a..02bfc8b1 100755 --- a/src/features/soundeffects.h +++ b/src/features/soundeffects.h @@ -13,8 +13,15 @@ struct SoundEffectsData unsigned int m_nLastReverseSoundTime = 0; float m_fBrakePressure = 0.0f; float m_fMaxPedal = 0.0f; + StreamHandle m_hDoorChimeStream = 0; SoundEffectsData(CVehicle *pVeh) {} - ~SoundEffectsData() {} + ~SoundEffectsData() + { + if (m_hDoorChimeStream) + { + AudioMgr::StopLoopStream(m_hDoorChimeStream); + } + } }; class SoundEffects : public CVehFeature diff --git a/src/utils/audiomgr.cpp b/src/utils/audiomgr.cpp index 00a99205..6ae35573 100755 --- a/src/utils/audiomgr.cpp +++ b/src/utils/audiomgr.cpp @@ -70,8 +70,8 @@ static float gfSoundMult = 1.0f; void AudioMgr::ReloadConfig() { - gbSoundEffectsEnabled = gConfig.ReadBoolean("SOUND", "SoundEffects", gConfig.ReadBoolean("FEATURES", "SoundEffects", false)); - gfSoundMult = gConfig.ReadFloat("SOUND", "SoundMult", gConfig.ReadFloat("TWEAKS", "SoundMult", 0.6f)); + gbSoundEffectsEnabled = gConfig.ReadBoolean("SOUND", "SoundEffects", false); + gfSoundMult = gConfig.ReadFloat("SOUND", "SoundMult", 0.6f); } void AudioMgr::Init() @@ -97,7 +97,7 @@ void AudioMgr::Init() Events::processScriptsEvent += [] { static bool bWasPaused = false; - bool bIsPaused = CTimer::m_UserPause || CTimer::m_CodePause; + bool bIsPaused = !Util::IsWindowFocused() || CTimer::m_UserPause || CTimer::m_CodePause; if (bIsPaused != bWasPaused) { @@ -169,7 +169,19 @@ void AudioMgr::PlaySwitchSound(CEntity *pEntity) bool AudioMgr::ShouldPlaySound() { - return gbSoundEffectsEnabled; + if (!gbSoundEffectsEnabled) + { + return false; + } + if (!Util::IsWindowFocused()) + { + return false; + } + if (CTimer::m_UserPause || CTimer::m_CodePause) + { + return false; + } + return true; } void AudioMgr::Play3DSound(const std::string &path, const CVector &worldPos, CEntity *pEntity, float baseVolume, float maxDistance) @@ -293,7 +305,7 @@ std::string AudioMgr::GetSirenAudioPath(int modeIndex) return ""; } -StreamHandle AudioMgr::PlaySirenStream(const std::string &path, const CVector &worldPos, float baseVolume, float maxDistance) +StreamHandle AudioMgr::PlayLoopStream(const std::string &path, const CVector &worldPos, float baseVolume, float maxDistance) { if (path.empty() || !BassAPI::bReady || !BassAPI::fnStreamCreate || !BassAPI::fnChannelPlay) { @@ -349,7 +361,7 @@ StreamHandle AudioMgr::PlaySirenStream(const std::string &path, const CVector &w return 0; } -void AudioMgr::UpdateSirenStream(StreamHandle stream, const CVector &worldPos, float baseVolume, float maxDistance) +void AudioMgr::UpdateLoopStream(StreamHandle stream, const CVector &worldPos, float baseVolume, float maxDistance) { if (!stream || !BassAPI::bReady || !BassAPI::fnChannelSetAttr || !BassAPI::fnChannelIsActive) { @@ -361,6 +373,12 @@ void AudioMgr::UpdateSirenStream(StreamHandle stream, const CVector &worldPos, f return; } + if (!ShouldPlaySound()) + { + BassAPI::fnChannelSetAttr(stream, 2 /* BASS_ATTRIB_VOL */, 0.0f); + return; + } + float distSq = MathUtil::DistanceSquared(worldPos, TheCamera.GetPosition()); float dist = std::sqrt(distSq); const float nearDist = 5.0f; @@ -388,7 +406,7 @@ void AudioMgr::UpdateSirenStream(StreamHandle stream, const CVector &worldPos, f BassAPI::fnChannelSetAttr(stream, 3 /* BASS_ATTRIB_PAN */, pan); } -void AudioMgr::StopSirenStream(StreamHandle &stream) +void AudioMgr::StopLoopStream(StreamHandle &stream) { if (!stream) { @@ -412,4 +430,19 @@ void AudioMgr::StopSirenStream(StreamHandle &stream) needToFree.erase(it); } stream = 0; +} + +StreamHandle AudioMgr::PlaySirenStream(const std::string &path, const CVector &worldPos, float baseVolume, float maxDistance) +{ + return PlayLoopStream(path, worldPos, baseVolume, maxDistance); +} + +void AudioMgr::UpdateSirenStream(StreamHandle stream, const CVector &worldPos, float baseVolume, float maxDistance) +{ + UpdateLoopStream(stream, worldPos, baseVolume, maxDistance); +} + +void AudioMgr::StopSirenStream(StreamHandle &stream) +{ + StopLoopStream(stream); } \ No newline at end of file diff --git a/src/utils/audiomgr.h b/src/utils/audiomgr.h index 517affc3..8d6e32b9 100755 --- a/src/utils/audiomgr.h +++ b/src/utils/audiomgr.h @@ -20,6 +20,9 @@ class AudioMgr static void PlayFileSound(const std::string &path, float volume = 1.0f); static void PlayClickSound(); static void PlaySwitchSound(CEntity *pEntity = nullptr); + static StreamHandle PlayLoopStream(const std::string &path, const CVector &worldPos, float baseVolume = 1.0f, float maxDistance = 40.0f); + static void UpdateLoopStream(StreamHandle stream, const CVector &worldPos, float baseVolume = 1.0f, float maxDistance = 40.0f); + static void StopLoopStream(StreamHandle &stream); static StreamHandle PlaySirenStream(const std::string &path, const CVector &worldPos, float baseVolume = 1.0f, float maxDistance = 120.0f); static void UpdateSirenStream(StreamHandle stream, const CVector &worldPos, float baseVolume = 1.0f, float maxDistance = 120.0f); static void StopSirenStream(StreamHandle &stream);