From 82d14737c1a0e968d0a807029560d2e3183dc318 Mon Sep 17 00:00:00 2001 From: Caner Karaca Date: Wed, 16 Sep 2026 17:10:01 +0300 Subject: [PATCH] feat(sound): add modern 3D car door open chime warning sound effect --- resource/dist/ModelExtras.ini | 15 ++- .../dist/ModelExtras/audio/door_chime.mp3 | Bin 0 -> 11598 bytes src/features/soundeffects.cpp | 114 +++++++++++++++++- src/features/soundeffects.h | 9 +- src/utils/audiomgr.cpp | 47 ++++++-- src/utils/audiomgr.h | 3 + 6 files changed, 168 insertions(+), 20 deletions(-) create mode 100644 resource/dist/ModelExtras/audio/door_chime.mp3 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 0000000000000000000000000000000000000000..5ce7e1a02ae081e870eceb1aa3f51a619c81ffd0 GIT binary patch literal 11598 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 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);