Index: source/simulation2/components/CCmpSoundManager.cpp =================================================================== --- source/simulation2/components/CCmpSoundManager.cpp (revision 19503) +++ source/simulation2/components/CCmpSoundManager.cpp (working copy) @@ -92,6 +92,36 @@ g_SoundManager->Pause(true); } + virtual void AddAmbientEmitter(const std::wstring& name, entity_id_t source) + { + if (!g_SoundManager) + return; + + CmpPtr cmpPosition(GetSimContext(), source); + if (!cmpPosition || !cmpPosition->IsInWorld()) + return; + + g_SoundManager->AddAmbientEmitter(name, CVector3D(cmpPosition->GetPosition()), source); + } + + virtual void RemoveAmbientEmitter(entity_id_t source) + { + if (!g_SoundManager) + return; + g_SoundManager->RemoveAmbientEmitter(source); + } + + virtual void UpdateAmbientEmitterPosition(entity_id_t source) + { + if (!g_SoundManager) + return; + + CmpPtr cmpPosition(GetSimContext(), source); + if (!cmpPosition || !cmpPosition->IsInWorld()) + return; + + g_SoundManager->UpdateAmbientEmitterPosition(source, CVector3D(cmpPosition->GetPosition())); + } }; REGISTER_COMPONENT_TYPE(SoundManager) Index: source/simulation2/components/ICmpSoundManager.cpp =================================================================== --- source/simulation2/components/ICmpSoundManager.cpp (revision 19503) +++ source/simulation2/components/ICmpSoundManager.cpp (working copy) @@ -23,5 +23,8 @@ BEGIN_INTERFACE_WRAPPER(SoundManager) DEFINE_INTERFACE_METHOD_2("PlaySoundGroup", void, ICmpSoundManager, PlaySoundGroup, std::wstring, entity_id_t) +DEFINE_INTERFACE_METHOD_2("AddAmbientEmitter", void, ICmpSoundManager, AddAmbientEmitter, std::wstring, entity_id_t) +DEFINE_INTERFACE_METHOD_1("RemoveAmbientEmitter", void, ICmpSoundManager, RemoveAmbientEmitter, entity_id_t) +DEFINE_INTERFACE_METHOD_1("UpdateAmbientEmitterPosition", void, ICmpSoundManager, UpdateAmbientEmitterPosition, entity_id_t) DEFINE_INTERFACE_METHOD_0("StopMusic", void, ICmpSoundManager, StopMusic) END_INTERFACE_WRAPPER(SoundManager) Index: source/simulation2/components/ICmpSoundManager.h =================================================================== --- source/simulation2/components/ICmpSoundManager.h (revision 19503) +++ source/simulation2/components/ICmpSoundManager.h (working copy) @@ -33,6 +33,12 @@ */ virtual void PlaySoundGroup(const std::wstring& name, entity_id_t source) = 0; + virtual void AddAmbientEmitter(const std::wstring& name, entity_id_t source) = 0; + + virtual void RemoveAmbientEmitter(entity_id_t source) = 0; + + virtual void UpdateAmbientEmitterPosition(entity_id_t source) = 0; + virtual void StopMusic() = 0; DECLARE_INTERFACE_TYPE(SoundManager) Index: source/soundmanager/SoundManager.cpp =================================================================== --- source/soundmanager/SoundManager.cpp (revision 19503) +++ source/soundmanager/SoundManager.cpp (working copy) @@ -24,11 +24,13 @@ #include "items/CSoundItem.h" #include "items/CStreamItem.h" +#include "graphics/GameView.h" #include "lib/external_libraries/libsdl.h" #include "ps/CLogger.h" #include "ps/CStr.h" #include "ps/ConfigDB.h" #include "ps/Filesystem.h" +#include "ps/Game.h" #include "ps/Profiler2.h" #include "ps/XML/Xeromyces.h" @@ -587,6 +589,13 @@ if (m_CurrentEnvirons) m_CurrentEnvirons->EnsurePlay(); + if (g_Game && g_Game->GetView()) + for (SAmbientEmitterData& ambientEmitter : m_AmbientEmitters) + { + ambientEmitter.soundItem->EnsurePlay(); + ambientEmitter.soundItem->SetGain(GetAmbientEmitterGain(ambientEmitter.position)); + } + if (m_Worker) m_Worker->CleanupItems(); } @@ -665,6 +674,62 @@ group->PlayNext(sourcePos, source); } +float CSoundManager::GetAmbientEmitterGain(CVector3D& emitterPos) +{ + CVector3D cameraPos(g_Game->GetView()->GetCameraPosX(), g_Game->GetView()->GetCameraPosY(), g_Game->GetView()->GetCameraPosZ()); + float itemDist = (emitterPos - cameraPos).Length(); + return std::max(1.0f - itemDist / 200.f, 0.f); // TODO: Use something better +} + +// TODO: Properly handle SoundGroups instead +void CSoundManager::AddAmbientEmitter(const VfsPath& groupPath, CVector3D& sourcePos, entity_id_t source) +{ + if (!m_Enabled) + return; + + ISoundItem* ambientItem = LoadItem(groupPath); + if (ambientItem == NULL) + return; + + // Use the ambient gain config option for ambient emitters + if (m_AmbientGain > 0) + { + ambientItem->SetGain(0); + ambientItem->PlayLoop(); + ambientItem->FadeToIn(GetAmbientEmitterGain(sourcePos), 2.00); + } + + SAmbientEmitterData ambientEmitter; + ambientEmitter.soundItem = ambientItem; + ambientEmitter.position = sourcePos; + ambientEmitter.id = source; + + m_AmbientEmitters.emplace_back(ambientEmitter); +} + +void CSoundManager::RemoveAmbientEmitter(entity_id_t source) +{ + // TODO: Make this function cleaner + for (SAmbientEmitterData& ambientEmitter : m_AmbientEmitters) + if (ambientEmitter.id == source) + ambientEmitter.soundItem->FadeAndDelete(3.00); + + m_AmbientEmitters.erase( + std::remove_if(m_AmbientEmitters.begin(), m_AmbientEmitters.end(), + [&](SAmbientEmitterData& emitterData) + { + return emitterData.id == source; + } + ), m_AmbientEmitters.end()); +} + +void CSoundManager::UpdateAmbientEmitterPosition(entity_id_t source, CVector3D& sourcePos) +{ + for (SAmbientEmitterData& ambientEmitter : m_AmbientEmitters) + if (ambientEmitter.id == source) + ambientEmitter.position = sourcePos; +} + void CSoundManager::PlayAsMusic(const VfsPath& itemPath, bool looping) { if (m_Enabled) @@ -713,6 +778,7 @@ PauseMusic(pauseIt); PauseAmbient(pauseIt); PauseAction(pauseIt); + PauseAmbientEmitters(pauseIt); } void CSoundManager::PauseMusic(bool pauseIt) @@ -740,6 +806,13 @@ m_AmbientPaused = pauseIt; } +void CSoundManager::PauseAmbientEmitters(bool pause) +{ + for (SAmbientEmitterData& emitterData : m_AmbientEmitters) + if (emitterData.soundItem) + pause ? emitterData.soundItem->Pause() : emitterData.soundItem->Resume(); +} + void CSoundManager::PauseAction(bool pauseIt) { m_ActionPaused = pauseIt; Index: source/soundmanager/SoundManager.h =================================================================== --- source/soundmanager/SoundManager.h (revision 19503) +++ source/soundmanager/SoundManager.h (working copy) @@ -63,6 +63,12 @@ ALSourceHolder* m_ALSourceBuffer; ISoundItem* m_CurrentTune; ISoundItem* m_CurrentEnvirons; + struct SAmbientEmitterData { + ISoundItem* soundItem; + CVector3D position; + entity_id_t id; + }; + std::vector m_AmbientEmitters; CSoundManagerWorker* m_Worker; CMutex m_DistressMutex; PlayList* m_PlayListItems; @@ -148,6 +154,7 @@ void Pause(bool pauseIt); void PauseMusic(bool pauseIt); void PauseAmbient(bool pauseIt); + void PauseAmbientEmitters(bool pause); void PauseAction(bool pauseIt); void SetAmbientItem(ISoundItem* anItem); @@ -157,6 +164,11 @@ void SetActionGain(float gain); void SetUIGain(float gain); + float CSoundManager::GetAmbientEmitterGain(CVector3D& emitterPos); + void AddAmbientEmitter(const VfsPath& groupPath, CVector3D& sourcePos, entity_id_t source); + void RemoveAmbientEmitter(entity_id_t source); + void UpdateAmbientEmitterPosition(entity_id_t source, CVector3D& sourcePos); + protected: void InitListener(); Status AlcInit(); Index: source/soundmanager/ISoundManager.h =================================================================== --- source/soundmanager/ISoundManager.h (revision 19503) +++ source/soundmanager/ISoundManager.h (working copy) @@ -49,6 +49,10 @@ virtual void PlayAsAmbient(const VfsPath& itemPath, bool looping) = 0; virtual void PlayAsGroup(const VfsPath& groupPath, CVector3D sourcePos, entity_id_t source, bool ownedSound) = 0; + virtual void AddAmbientEmitter(const VfsPath& groupPath, CVector3D& sourcePos, entity_id_t source) = 0; + virtual void RemoveAmbientEmitter(entity_id_t source) = 0; + virtual void UpdateAmbientEmitterPosition(entity_id_t source, CVector3D& sourcePos) = 0; + virtual bool InDistress() = 0; }; Index: binaries/data/mods/public/simulation/components/Sound.js =================================================================== --- binaries/data/mods/public/simulation/components/Sound.js (revision 19503) +++ binaries/data/mods/public/simulation/components/Sound.js (working copy) @@ -8,6 +8,7 @@ "actor/human/movement/walk.xml" + "attack/weapon/sword.xml" + "actor/human/death/death.xml" + + "ambient/water/river_slow.xml" + "" + "" + "" + @@ -21,6 +22,12 @@ Sound.prototype.Init = function() { + this.hasAmbientEmitter = !!this.template.SoundGroups.ambientEmitter; + if (this.hasAmbientEmitter) + { + let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager); + cmpSoundManager.AddAmbientEmitter(this.template.SoundGroups.ambientEmitter, this.entity); + } }; Sound.prototype.Serialize = null; // we have no dynamic state to save @@ -50,4 +57,23 @@ } }; +Sound.prototype.OnOwnershipChanged = function(msg) +{ + if (!this.hasAmbientEmitter || msg.to != -1) + return; + + let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager); + cmpSoundManager.RemoveAmbientEmitter(this.entity); +}; + +// This may be performace-intensive for moving ents with ambient emitters +Sound.prototype.OnPositionChanged = function(msg) +{ + if (!this.hasAmbientEmitter || !msg.inWorld) + return; + + let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager); + cmpSoundManager.UpdateAmbientEmitterPosition(this.entity); +}; + Engine.RegisterComponentType(IID_Sound, "Sound", Sound);