Upgrade SMSG_PLAY_OBJECT_SOUND/SPELL_IMPACT to 3D positional audio

Add PlayPositionalSoundCallback that carries both soundId and sourceGuid.
In Application, look up the source entity position and play via
AudioEngine::playSound3D(); fall back to playSound2D() when the entity
is unknown. Also read the 8-byte sourceGuid field from the packet
(previously the full 12-byte payload was ignored).
This commit is contained in:
Kelsi 2026-03-09 16:16:39 -07:00
parent 0913146f54
commit 97192ab2a4
3 changed files with 43 additions and 2 deletions

View file

@ -948,6 +948,11 @@ public:
using PlaySoundCallback = std::function<void(uint32_t soundId)>;
void setPlaySoundCallback(PlaySoundCallback cb) { playSoundCallback_ = std::move(cb); }
// Server-triggered 3-D positional sound callback — fires for SMSG_PLAY_OBJECT_SOUND and
// SMSG_PLAY_SPELL_IMPACT. Includes sourceGuid so the receiver can look up world position.
using PlayPositionalSoundCallback = std::function<void(uint32_t soundId, uint64_t sourceGuid)>;
void setPlayPositionalSoundCallback(PlayPositionalSoundCallback cb) { playPositionalSoundCallback_ = std::move(cb); }
// Mount state
using MountCallback = std::function<void(uint32_t mountDisplayId)>; // 0 = dismount
void setMountCallback(MountCallback cb) { mountCallback_ = std::move(cb); }
@ -2103,6 +2108,7 @@ private:
// ---- Server-triggered audio ----
PlayMusicCallback playMusicCallback_;
PlaySoundCallback playSoundCallback_;
PlayPositionalSoundCallback playPositionalSoundCallback_;
};
} // namespace game

View file

@ -2142,6 +2142,35 @@ void Application::setupUICallbacks() {
}
});
// SMSG_PLAY_OBJECT_SOUND / SMSG_PLAY_SPELL_IMPACT: play as 3D positional sound at source entity
gameHandler->setPlayPositionalSoundCallback([this](uint32_t soundId, uint64_t sourceGuid) {
if (!assetManager || !gameHandler) return;
auto dbc = assetManager->loadDBC("SoundEntries.dbc");
if (!dbc || !dbc->isLoaded()) return;
int32_t idx = dbc->findRecordById(soundId);
if (idx < 0) return;
const uint32_t row = static_cast<uint32_t>(idx);
std::string dir = dbc->getString(row, 23);
for (uint32_t f = 3; f <= 12; ++f) {
std::string name = dbc->getString(row, f);
if (name.empty()) continue;
std::string path = dir.empty() ? name : dir + "\\" + name;
// Play as 3D sound if source entity position is available
auto entity = gameHandler->getEntityManager().getEntity(sourceGuid);
if (entity) {
glm::vec3 pos{entity->getLatestX(), entity->getLatestY(), entity->getLatestZ()};
audio::AudioEngine::instance().playSound3D(path, pos);
} else {
audio::AudioEngine::instance().playSound2D(path);
}
return;
}
});
// Other player level-up callback — trigger 3D effect + chat notification
gameHandler->setOtherPlayerLevelUpCallback([this](uint64_t guid, uint32_t newLevel) {
if (!gameHandler || !renderer) return;

View file

@ -4507,9 +4507,15 @@ void GameHandler::handlePacket(network::Packet& packet) {
// ---- Play object/spell sounds ----
case Opcode::SMSG_PLAY_OBJECT_SOUND:
case Opcode::SMSG_PLAY_SPELL_IMPACT:
if (packet.getSize() - packet.getReadPos() >= 4) {
if (packet.getSize() - packet.getReadPos() >= 12) {
// uint32 soundId + uint64 sourceGuid
uint32_t soundId = packet.readUInt32();
uint64_t srcGuid = packet.readUInt64();
LOG_DEBUG("SMSG_PLAY_OBJECT_SOUND/SPELL_IMPACT id=", soundId, " src=0x", std::hex, srcGuid, std::dec);
if (playPositionalSoundCallback_) playPositionalSoundCallback_(soundId, srcGuid);
else if (playSoundCallback_) playSoundCallback_(soundId);
} else if (packet.getSize() - packet.getReadPos() >= 4) {
uint32_t soundId = packet.readUInt32();
LOG_DEBUG("SMSG_PLAY_OBJECT_SOUND/SPELL_IMPACT id=", soundId);
if (playSoundCallback_) playSoundCallback_(soundId);
}
packet.setReadPos(packet.getSize());