diff --git a/include/audio/music_manager.hpp b/include/audio/music_manager.hpp index 9a5bceea..dece08ed 100644 --- a/include/audio/music_manager.hpp +++ b/include/audio/music_manager.hpp @@ -52,6 +52,11 @@ private: float fadeInTimer = 0.0f; float fadeInDuration = 0.0f; float fadeInTargetVolume = 0.0f; + // Fade-out state (for stopMusic with fadeMs > 0) + bool fadingOut = false; + float fadeOutTimer = 0.0f; + float fadeOutDuration = 0.0f; + float fadeOutStartVolume = 0.0f; std::unordered_map> musicDataCache_; }; diff --git a/src/audio/music_manager.cpp b/src/audio/music_manager.cpp index 8ccff4f2..c22e6d68 100644 --- a/src/audio/music_manager.cpp +++ b/src/audio/music_manager.cpp @@ -144,15 +144,24 @@ void MusicManager::playFilePath(const std::string& filePath, bool loop, float fa } void MusicManager::stopMusic(float fadeMs) { - (void)fadeMs; // Fade not implemented yet - AudioEngine::instance().stopMusic(); - playing = false; + if (!playing) return; + fadingIn = false; - fadeInTimer = 0.0f; - fadeInDuration = 0.0f; - fadeInTargetVolume = 0.0f; - currentTrack.clear(); - currentTrackIsFile = false; + crossfading = false; + + if (fadeMs > 0.0f) { + // Begin fade-out; actual stop happens once volume reaches zero in update() + fadingOut = true; + fadeOutTimer = 0.0f; + fadeOutDuration = fadeMs / 1000.0f; + fadeOutStartVolume = effectiveMusicVolume(); + } else { + AudioEngine::instance().stopMusic(); + playing = false; + fadingOut = false; + currentTrack.clear(); + currentTrackIsFile = false; + } } void MusicManager::setVolume(int volume) { @@ -224,6 +233,22 @@ void MusicManager::update(float deltaTime) { playing = false; } + if (fadingOut) { + fadeOutTimer += deltaTime; + float t = std::clamp(1.0f - fadeOutTimer / std::max(fadeOutDuration, 0.001f), 0.0f, 1.0f); + AudioEngine::instance().setMusicVolume(fadeOutStartVolume * t); + if (t <= 0.0f) { + // Fade complete — stop playback and restore volume for next track + fadingOut = false; + AudioEngine::instance().stopMusic(); + AudioEngine::instance().setMusicVolume(effectiveMusicVolume()); + playing = false; + currentTrack.clear(); + currentTrackIsFile = false; + } + return; // Don't process other fade logic while fading out + } + if (fadingIn) { fadeInTimer += deltaTime; float t = std::clamp(fadeInTimer / std::max(fadeInDuration, 0.001f), 0.0f, 1.0f); diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 69961140..c83563f0 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -2131,9 +2131,8 @@ network::Packet RequestRaidInfoPacket::build() { // ============================================================ network::Packet DuelProposedPacket::build(uint64_t targetGuid) { - // TODO: Duels are initiated via CMSG_CAST_SPELL with spell 7266, - // not a dedicated CMSG_DUEL_PROPOSED opcode (which doesn't exist in WoW). - // For now, build a cast spell packet targeting the opponent. + // Duels are initiated via CMSG_CAST_SPELL with spell 7266 (Duel) targeted at the opponent. + // There is no separate CMSG_DUEL_PROPOSED opcode in WoW. auto packet = CastSpellPacket::build(7266, targetGuid, 0); LOG_DEBUG("Built duel request (spell 7266) for target: 0x", std::hex, targetGuid, std::dec); return packet;