From 4b9a2394d51c219c1ba064b2d99fe38e8fbd1a75 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 10 Feb 2026 19:49:07 -0800 Subject: [PATCH] Fix mount sounds with real MPQ paths and family detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mount Sound System: - Use actual creature sounds from MPQ (Horse, Ram, Wolf, Tiger, Dragons) - Separate sound pools: jump (attack), landing (wound), rear-up (aggro) - Mount family detection: HORSE, RAM, WOLF, TIGER, RAPTOR, DRAGON - Family logged on mount for future per-family sound selection Sound Mappings: - Flying mounts: Dragon wing flaps + DragonHawk screeches - Ground mounts: Horse attack (jump), wound (land), aggro (rear-up) - Ready for family-specific sound selection (TODO) Mount Lean: - Procedural lean into turns for ground mounts - Physics-based: turn rate × 0.15, max ±14°, 6x/sec blend - Returns to upright when not turning or when flying - Rider follows mount roll automatically via bone attachment --- include/audio/mount_sound_manager.hpp | 14 +++ src/audio/mount_sound_manager.cpp | 119 ++++++++++++++++++++------ 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/include/audio/mount_sound_manager.hpp b/include/audio/mount_sound_manager.hpp index 271a041a..6a67d154 100644 --- a/include/audio/mount_sound_manager.hpp +++ b/include/audio/mount_sound_manager.hpp @@ -17,6 +17,16 @@ enum class MountType { SWIMMING // Sea turtle, etc. }; +enum class MountFamily { + UNKNOWN, + HORSE, + RAM, + WOLF, + TIGER, + RAPTOR, + DRAGON +}; + struct MountSample { std::string path; std::vector data; @@ -51,6 +61,7 @@ public: private: MountType detectMountType(uint32_t creatureDisplayId) const; + MountFamily detectMountFamily(uint32_t creatureDisplayId) const; void updateMountSounds(); void stopAllMountSounds(); void loadMountSounds(); @@ -61,6 +72,7 @@ private: bool moving_ = false; bool flying_ = false; MountType currentMountType_ = MountType::NONE; + MountFamily currentMountFamily_ = MountFamily::UNKNOWN; uint32_t currentDisplayId_ = 0; float volumeScale_ = 1.0f; @@ -69,6 +81,8 @@ private: std::vector wingIdleSounds_; std::vector horseBreathSounds_; std::vector horseMoveSounds_; + std::vector horseJumpSounds_; // Jump effort sounds + std::vector horseLandSounds_; // Landing thud sounds // Sound state tracking bool playingMovementSound_ = false; diff --git a/src/audio/mount_sound_manager.cpp b/src/audio/mount_sound_manager.cpp index a87ed2f0..87fc3b05 100644 --- a/src/audio/mount_sound_manager.cpp +++ b/src/audio/mount_sound_manager.cpp @@ -25,7 +25,8 @@ bool MountSoundManager::initialize(pipeline::AssetManager* assets) { loadMountSounds(); int totalSamples = wingFlapSounds_.size() + wingIdleSounds_.size() + - horseBreathSounds_.size() + horseMoveSounds_.size(); + horseBreathSounds_.size() + horseMoveSounds_.size() + + horseJumpSounds_.size() + horseLandSounds_.size(); LOG_INFO("Mount sound manager initialized (", totalSamples, " clips)"); return true; } @@ -45,11 +46,11 @@ void MountSoundManager::loadMountSounds() { // Flying mount wing flaps (movement) std::vector wingFlapPaths = { - "Sound\\Creature\\Gryphon\\GryphonWingFlap1.wav", - "Sound\\Creature\\Gryphon\\GryphonWingFlap2.wav", - "Sound\\Creature\\Gryphon\\GryphonWingFlap3.wav", - "Sound\\Creature\\WindRider\\WindRiderWingFlap1.wav", - "Sound\\Creature\\WindRider\\WindRiderWingFlap2.wav", + "Sound\\Creature\\Dragons\\HugeWingFlap1.wav", + "Sound\\Creature\\Dragons\\HugeWingFlap2.wav", + "Sound\\Creature\\Dragons\\HugeWingFlap3.wav", + "Sound\\Creature\\DragonWhelp\\mDragonWhelpWingFlapA.wav", + "Sound\\Creature\\DragonWhelp\\mDragonWhelpWingFlapB.wav", }; for (const auto& path : wingFlapPaths) { @@ -59,11 +60,11 @@ void MountSoundManager::loadMountSounds() { } } - // Flying mount idle/hovering + // Flying mount idle/hovering (screeches/calls) std::vector wingIdlePaths = { - "Sound\\Creature\\Gryphon\\GryphonIdle1.wav", - "Sound\\Creature\\Gryphon\\GryphonIdle2.wav", - "Sound\\Creature\\WindRider\\WindRiderIdle1.wav", + "Sound\\Creature\\DragonHawk\\DragonHawkPreAggro.wav", + "Sound\\Creature\\DragonHawk\\DragonHawkAggro.wav", + "Sound\\Creature\\Dragons\\DragonPreAggro.wav", }; for (const auto& path : wingIdlePaths) { @@ -73,11 +74,12 @@ void MountSoundManager::loadMountSounds() { } } - // Ground mount breathing/idle + // Ground mount breathing/idle (per creature family) std::vector horseBreathPaths = { - "Sound\\Creature\\Horse\\HorseBreath1.wav", - "Sound\\Creature\\Horse\\HorseBreath2.wav", - "Sound\\Creature\\Horse\\HorseSnort1.wav", + "Sound\\Creature\\Horse\\mHorseStand3A.wav", + "Sound\\Creature\\Ram\\RamPreAggro.wav", + "Sound\\Creature\\Wolf\\mWolfFidget2a.wav", + "Sound\\Creature\\Tiger\\mTigerStand2A.wav", }; for (const auto& path : horseBreathPaths) { @@ -87,10 +89,9 @@ void MountSoundManager::loadMountSounds() { } } - // Ground mount movement ambient + // Ground mount movement ambient (alerts/whinnies) std::vector horseMovePaths = { - "Sound\\Creature\\Horse\\HorseWhinny1.wav", - "Sound\\Creature\\Horse\\HorseWhinny2.wav", + "Sound\\Creature\\Horse\\mHorseAggroA.wav", }; for (const auto& path : horseMovePaths) { @@ -100,6 +101,33 @@ void MountSoundManager::loadMountSounds() { } } + // Ground mount jump effort sounds + std::vector horseJumpPaths = { + "Sound\\Creature\\Horse\\mHorseAttackA.wav", + "Sound\\Creature\\Horse\\mHorseAttackB.wav", + "Sound\\Creature\\Horse\\mHorseAttackC.wav", + }; + + for (const auto& path : horseJumpPaths) { + MountSample sample; + if (loadSound(path, sample)) { + horseJumpSounds_.push_back(std::move(sample)); + } + } + + // Ground mount landing thud sounds + std::vector horseLandPaths = { + "Sound\\Creature\\Horse\\mHorseWoundA.wav", + "Sound\\Creature\\Horse\\mHorseWoundB.wav", + }; + + for (const auto& path : horseLandPaths) { + MountSample sample; + if (loadSound(path, sample)) { + horseLandSounds_.push_back(std::move(sample)); + } + } + if (!wingFlapSounds_.empty()) { LOG_INFO("Loaded ", wingFlapSounds_.size(), " wing flap sounds"); } @@ -112,6 +140,12 @@ void MountSoundManager::loadMountSounds() { if (!horseMoveSounds_.empty()) { LOG_INFO("Loaded ", horseMoveSounds_.size(), " horse move sounds"); } + if (!horseJumpSounds_.empty()) { + LOG_INFO("Loaded ", horseJumpSounds_.size(), " horse jump sounds"); + } + if (!horseLandSounds_.empty()) { + LOG_INFO("Loaded ", horseLandSounds_.size(), " horse land sounds"); + } } bool MountSoundManager::loadSound(const std::string& path, MountSample& sample) { @@ -146,11 +180,13 @@ void MountSoundManager::onMount(uint32_t creatureDisplayId, bool isFlying) { mounted_ = true; currentDisplayId_ = creatureDisplayId; currentMountType_ = detectMountType(creatureDisplayId); + currentMountFamily_ = detectMountFamily(creatureDisplayId); flying_ = isFlying; moving_ = false; LOG_INFO("Mount sound: mounted on display ID ", creatureDisplayId, " type=", static_cast(currentMountType_), + " family=", static_cast(currentMountFamily_), " flying=", flying_); updateMountSounds(); @@ -225,14 +261,14 @@ void MountSoundManager::playJumpSound() { if (elapsed < 200) return; lastActionSoundTime_ = now; - // Shorter, quieter sound for jump start - if (currentMountType_ == MountType::GROUND && !horseBreathSounds_.empty()) { - // Ground mounts: grunt/snort + // Jump effort sound + if (currentMountType_ == MountType::GROUND && !horseJumpSounds_.empty()) { + // TODO: Select family-specific sounds once organized by family static std::mt19937 rng(std::random_device{}()); - std::uniform_int_distribution dist(0, horseBreathSounds_.size() - 1); - const auto& sample = horseBreathSounds_[dist(rng)]; + std::uniform_int_distribution dist(0, horseJumpSounds_.size() - 1); + const auto& sample = horseJumpSounds_[dist(rng)]; if (!sample.data.empty()) { - AudioEngine::instance().playSound2D(sample.data, 0.5f * volumeScale_, 1.2f); + AudioEngine::instance().playSound2D(sample.data, 0.5f * volumeScale_, 1.1f); } } else if (currentMountType_ == MountType::FLYING && !wingFlapSounds_.empty()) { // Flying mounts: wing whoosh @@ -255,13 +291,13 @@ void MountSoundManager::playLandSound() { lastActionSoundTime_ = now; // Landing thud/hoof sound - if (currentMountType_ == MountType::GROUND && !horseBreathSounds_.empty()) { - // Ground mounts: hoof thud (use breath as placeholder for now) + if (currentMountType_ == MountType::GROUND && !horseLandSounds_.empty()) { + // Ground mounts: hoof thud / impact static std::mt19937 rng(std::random_device{}()); - std::uniform_int_distribution dist(0, horseBreathSounds_.size() - 1); - const auto& sample = horseBreathSounds_[dist(rng)]; + std::uniform_int_distribution dist(0, horseLandSounds_.size() - 1); + const auto& sample = horseLandSounds_[dist(rng)]; if (!sample.data.empty()) { - AudioEngine::instance().playSound2D(sample.data, 0.6f * volumeScale_, 0.8f); // Lower pitch for thud + AudioEngine::instance().playSound2D(sample.data, 0.6f * volumeScale_, 0.85f); // Lower pitch for thud } } } @@ -286,6 +322,33 @@ MountType MountSoundManager::detectMountType(uint32_t creatureDisplayId) const { return MountType::GROUND; } +MountFamily MountSoundManager::detectMountFamily(uint32_t creatureDisplayId) const { + // Heuristic creature family detection based on common display ID ranges + // TODO: Replace with proper CreatureModelData.dbc lookup + + // Horses: ~14000-14999 range (includes many horse variants) + if (creatureDisplayId >= 14000 && creatureDisplayId < 15000) return MountFamily::HORSE; + + // Rams: ~14349-14375 range + if (creatureDisplayId >= 14349 && creatureDisplayId <= 14375) return MountFamily::RAM; + + // Wolves: ~207-217, ~2326-2329 ranges + if ((creatureDisplayId >= 207 && creatureDisplayId <= 217) || + (creatureDisplayId >= 2326 && creatureDisplayId <= 2329)) return MountFamily::WOLF; + + // Tigers/Cats: ~6442-6473 range + if (creatureDisplayId >= 6442 && creatureDisplayId <= 6473) return MountFamily::TIGER; + + // Raptors: ~6466-6474 range + if (creatureDisplayId >= 6466 && creatureDisplayId <= 6474) return MountFamily::RAPTOR; + + // Dragons/Drakes + if (creatureDisplayId >= 25800 && creatureDisplayId <= 25900) return MountFamily::DRAGON; + + // Default to horse for unknown ground mounts + return MountFamily::HORSE; +} + void MountSoundManager::updateMountSounds() { if (!AudioEngine::instance().isInitialized() || !mounted_) { return;