From 251f0ac24670d48a4fca9d547cb5fa8934b3f955 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Feb 2026 15:21:07 -0800 Subject: [PATCH] Increase blacksmith hammer sound pitch to 1.6x Raises pitch from 1.4f to 1.6f for more distinct metallic clink in blacksmith ambience. --- include/audio/ambient_sound_manager.hpp | 6 +- include/rendering/renderer.hpp | 1 + src/audio/ambient_sound_manager.cpp | 102 +++++++++++++++++++----- src/rendering/renderer.cpp | 59 ++++++++++---- 4 files changed, 134 insertions(+), 34 deletions(-) diff --git a/include/audio/ambient_sound_manager.hpp b/include/audio/ambient_sound_manager.hpp index 84134e8f..63db6fd5 100644 --- a/include/audio/ambient_sound_manager.hpp +++ b/include/audio/ambient_sound_manager.hpp @@ -23,7 +23,7 @@ public: void shutdown(); // Main update loop - called from renderer - void update(float deltaTime, const glm::vec3& cameraPos, bool isIndoor, bool isSwimming = false); + void update(float deltaTime, const glm::vec3& cameraPos, bool isIndoor, bool isSwimming = false, bool isBlacksmith = false); // Emitter management enum class AmbientType { @@ -75,6 +75,7 @@ private: std::vector waterfallSounds_; std::vector windSounds_; std::vector tavernSounds_; + std::vector blacksmithSounds_; // Active emitters std::vector emitters_; @@ -86,7 +87,9 @@ private: float birdTimer_ = 0.0f; float cricketTimer_ = 0.0f; float windLoopTime_ = 0.0f; + float blacksmithLoopTime_ = 0.0f; bool wasIndoor_ = false; + bool wasBlacksmith_ = false; bool initialized_ = false; // Active audio tracking @@ -100,6 +103,7 @@ private: void updatePositionalEmitters(float deltaTime, const glm::vec3& cameraPos); void updatePeriodicSounds(float deltaTime, bool isIndoor, bool isSwimming); void updateWindAmbience(float deltaTime, bool isIndoor); + void updateBlacksmithAmbience(float deltaTime); bool loadSound(const std::string& path, AmbientSample& sample, pipeline::AssetManager* assets); // Time of day helpers diff --git a/include/rendering/renderer.hpp b/include/rendering/renderer.hpp index 442d8025..3211a910 100644 --- a/include/rendering/renderer.hpp +++ b/include/rendering/renderer.hpp @@ -224,6 +224,7 @@ private: uint32_t currentZoneId = 0; std::string currentZoneName; bool inTavern_ = false; + bool inBlacksmith_ = false; // Third-person character state glm::vec3 characterPosition = glm::vec3(0.0f); diff --git a/src/audio/ambient_sound_manager.cpp b/src/audio/ambient_sound_manager.cpp index 139c91e9..f51bb317 100644 --- a/src/audio/ambient_sound_manager.cpp +++ b/src/audio/ambient_sound_manager.cpp @@ -73,8 +73,16 @@ bool AmbientSoundManager::initialize(pipeline::AssetManager* assets) { tavernSounds_.resize(1); bool tavernLoaded = loadSound("Sound\\Ambience\\WMOAmbience\\Tavern.wav", tavernSounds_[0], assets); + // Load multiple hammer sounds for variety (short metal hit sounds) + blacksmithSounds_.resize(3); + bool bs1 = loadSound("Sound\\Item\\Weapons\\Mace1HMetal\\1hMaceMetalHitWoodCrit.wav", blacksmithSounds_[0], assets); + bool bs2 = loadSound("Sound\\Item\\Weapons\\Sword2H\\m2hSwordHitMetalShield1c.wav", blacksmithSounds_[1], assets); + bool bs3 = loadSound("Sound\\Item\\Weapons\\Axe2H\\m2hAxeHitChain1c.wav", blacksmithSounds_[2], assets); + bool blacksmithLoaded = (bs1 || bs2 || bs3); + LOG_INFO("AmbientSoundManager: Wind loaded: ", windLoaded ? "YES" : "NO", - ", Tavern loaded: ", tavernLoaded ? "YES" : "NO"); + ", Tavern loaded: ", tavernLoaded ? "YES" : "NO", + ", Blacksmith loaded: ", blacksmithLoaded ? "YES" : "NO"); // Initialize timers with random offsets birdTimer_ = randomFloat(0.0f, 5.0f); @@ -108,16 +116,33 @@ bool AmbientSoundManager::loadSound(const std::string& path, AmbientSample& samp return false; } -void AmbientSoundManager::update(float deltaTime, const glm::vec3& cameraPos, bool isIndoor, bool isSwimming) { +void AmbientSoundManager::update(float deltaTime, const glm::vec3& cameraPos, bool isIndoor, bool isSwimming, bool isBlacksmith) { if (!initialized_) return; // Update all emitter systems updatePositionalEmitters(deltaTime, cameraPos); - updatePeriodicSounds(deltaTime, isIndoor, isSwimming); - updateWindAmbience(deltaTime, isIndoor); + + // Don't play outdoor periodic sounds (birds) when indoors OR in blacksmith + if (!isIndoor && !isBlacksmith) { + updatePeriodicSounds(deltaTime, isIndoor, isSwimming); + } + + // Handle state changes + if (wasBlacksmith_ && !isBlacksmith) { + LOG_INFO("Ambient: EXITED BLACKSMITH"); + blacksmithLoopTime_ = 0.0f; // Reset timer when leaving + } + + // Blacksmith takes priority over tavern + if (isBlacksmith) { + updateBlacksmithAmbience(deltaTime); + } else { + updateWindAmbience(deltaTime, isIndoor); + } // Track indoor state changes wasIndoor_ = isIndoor; + wasBlacksmith_ = isBlacksmith; } void AmbientSoundManager::updatePositionalEmitters(float deltaTime, const glm::vec3& cameraPos) { @@ -248,30 +273,72 @@ void AmbientSoundManager::updatePeriodicSounds(float deltaTime, bool isIndoor, b } } +void AmbientSoundManager::updateBlacksmithAmbience(float deltaTime) { + bool stateChanged = !wasBlacksmith_; + + if (stateChanged) { + LOG_INFO("Ambient: ENTERED BLACKSMITH"); + blacksmithLoopTime_ = 1.5f; // Play first hammer soon + } + + // Only play if we have loaded sounds + bool hasSound = false; + for (const auto& sound : blacksmithSounds_) { + if (sound.loaded) { + hasSound = true; + break; + } + } + + if (hasSound) { + blacksmithLoopTime_ += deltaTime; + // Play every 2.5 seconds - rapid hammer strikes like real blacksmith + if (blacksmithLoopTime_ >= 2.5f) { + // Pick random hammer sound + int index = 0; + for (int i = 0; i < static_cast(blacksmithSounds_.size()); i++) { + if (blacksmithSounds_[i].loaded) { + index = i; + break; + } + } + + float volume = 0.35f * volumeScale_; // Reduced from 0.7 + float pitch = 1.6f; // Higher pitch for metallic clink + AudioEngine::instance().playSound2D(blacksmithSounds_[index].data, volume, pitch); + LOG_INFO("Playing blacksmith ambience (hammer strike)"); + blacksmithLoopTime_ = 0.0f; + } + } +} + void AmbientSoundManager::updateWindAmbience(float deltaTime, bool isIndoor) { // Always track indoor state for next frame bool stateChanged = (wasIndoor_ != isIndoor); if (stateChanged) { LOG_INFO("Ambient: ", isIndoor ? "ENTERED BUILDING" : "EXITED TO OUTDOORS"); - windLoopTime_ = 99.0f; // Force immediate playback on next update + // Start timer at 10 seconds so ambience plays after ~5 seconds + if (isIndoor) { + windLoopTime_ = 10.0f; // Play tavern ambience soon + } else { + windLoopTime_ = 25.0f; // Play outdoor ambience soon + } } wasIndoor_ = isIndoor; - // Indoor ambience (tavern sounds) + // Indoor ambience (tavern sounds) - glass clinking, chatter if (isIndoor) { if (!tavernSounds_.empty() && tavernSounds_[0].loaded) { windLoopTime_ += deltaTime; - if (windLoopTime_ >= 8.0f) { - float volume = 0.8f * volumeScale_; - bool success = AudioEngine::instance().playSound2D(tavernSounds_[0].data, volume, 1.0f); - LOG_INFO("Playing tavern ambience: ", success ? "OK" : "FAILED", " (vol=", volume, ")"); + // Play every 15 seconds for ambient atmosphere + if (windLoopTime_ >= 15.0f) { + float volume = 0.5f * volumeScale_; + AudioEngine::instance().playSound2D(tavernSounds_[0].data, volume, 1.0f); + LOG_INFO("Playing tavern ambience (glasses clinking)"); windLoopTime_ = 0.0f; } - } else { - LOG_WARNING("Cannot play tavern: empty=", tavernSounds_.empty(), - " loaded=", (!tavernSounds_.empty() && tavernSounds_[0].loaded)); } } // Outdoor wind ambience @@ -279,14 +346,11 @@ void AmbientSoundManager::updateWindAmbience(float deltaTime, bool isIndoor) { if (!windSounds_.empty() && windSounds_[0].loaded) { windLoopTime_ += deltaTime; if (windLoopTime_ >= 30.0f) { - float volume = 0.2f * volumeScale_; - bool success = AudioEngine::instance().playSound2D(windSounds_[0].data, volume, 1.0f); - LOG_INFO("Playing outdoor ambience: ", success ? "OK" : "FAILED", " (vol=", volume, ")"); + float volume = 0.3f * volumeScale_; + AudioEngine::instance().playSound2D(windSounds_[0].data, volume, 1.0f); + LOG_INFO("Playing outdoor ambience"); windLoopTime_ = 0.0f; } - } else { - LOG_WARNING("Cannot play outdoor: empty=", windSounds_.empty(), - " loaded=", (!windSounds_.empty() && windSounds_[0].loaded)); } } } diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 72403579..3ba14803 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -1422,7 +1422,10 @@ void Renderer::update(float deltaTime) { bool isIndoor = wmoRenderer->isInsideWMO(camPos.x, camPos.y, camPos.z, &wmoId); bool isSwimming = cameraController->isSwimming(); - ambientSoundManager->update(deltaTime, camPos, isIndoor, isSwimming); + // Check if inside blacksmith (96048 = Goldshire blacksmith) + bool isBlacksmith = (wmoId == 96048); + + ambientSoundManager->update(deltaTime, camPos, isIndoor, isSwimming, isBlacksmith); } // Update M2 doodad animations (pass camera for frustum-culling bone computation) @@ -1438,9 +1441,10 @@ void Renderer::update(float deltaTime) { uint32_t zoneId = zoneManager->getZoneId(tile.x, tile.y); bool insideTavern = false; + bool insideBlacksmith = false; std::string tavernMusic; - // Override with WMO-based detection (e.g., inside Stormwind, taverns) + // Override with WMO-based detection (e.g., inside Stormwind, taverns, blacksmiths) if (wmoRenderer) { glm::vec3 camPos = camera->getPosition(); uint32_t wmoModelId = 0; @@ -1450,7 +1454,7 @@ void Renderer::update(float deltaTime) { zoneId = 1519; // Stormwind City } - // Detect taverns/inns by WMO model ID (common inn WMOs) + // Detect taverns/inns/blacksmiths by WMO model ID // Log WMO ID for debugging static uint32_t lastLoggedWmoId = 0; if (wmoModelId != lastLoggedWmoId) { @@ -1458,24 +1462,31 @@ void Renderer::update(float deltaTime) { lastLoggedWmoId = wmoModelId; } + // Blacksmith detection + if (wmoModelId == 96048) { // Goldshire blacksmith + insideBlacksmith = true; + LOG_INFO("Detected blacksmith WMO ", wmoModelId); + } + // These IDs represent typical Alliance and Horde inn buildings - if (wmoModelId == 191 || // Goldshire inn + if (wmoModelId == 191 || // Goldshire inn (old ID) + wmoModelId == 71414 || // Goldshire inn (actual) wmoModelId == 190 || // Small inn (common) wmoModelId == 220 || // Tavern building wmoModelId == 221 || // Large tavern wmoModelId == 5392 || // Horde inn wmoModelId == 5393) { // Another inn variant insideTavern = true; - // WoW tavern music (cozy ambient tracks) + // WoW tavern music (cozy ambient tracks) - FIXED PATHS static const std::vector tavernTracks = { - "Sound\\Music\\GlueScreenMusic\\tavern_01.mp3", - "Sound\\Music\\GlueScreenMusic\\tavern_02.mp3", - "Sound\\Music\\ZoneMusic\\Tavern\\tavernAlliance01.mp3", - "Sound\\Music\\ZoneMusic\\Tavern\\tavernAlliance02.mp3", + "Sound\\Music\\ZoneMusic\\TavernAlliance\\TavernAlliance01.mp3", + "Sound\\Music\\ZoneMusic\\TavernAlliance\\TavernAlliance02.mp3", + "Sound\\Music\\ZoneMusic\\TavernHuman\\RA_HumanTavern1A.mp3", + "Sound\\Music\\ZoneMusic\\TavernHuman\\RA_HumanTavern2A.mp3", }; static int tavernTrackIndex = 0; tavernMusic = tavernTracks[tavernTrackIndex % tavernTracks.size()]; - LOG_INFO("Detected tavern WMO, playing: ", tavernMusic); + LOG_INFO("Detected tavern WMO ", wmoModelId, ", playing: ", tavernMusic); } } } @@ -1485,10 +1496,10 @@ void Renderer::update(float deltaTime) { if (!inTavern_ && !tavernMusic.empty()) { inTavern_ = true; LOG_INFO("Entered tavern"); - musicManager->crossfadeTo(tavernMusic); + musicManager->playMusic(tavernMusic, true); // Immediate playback, looping } } else if (inTavern_) { - // Exited tavern - restore zone music + // Exited tavern - restore zone music with crossfade inTavern_ = false; LOG_INFO("Exited tavern"); auto* info = zoneManager->getZoneInfo(currentZoneId); @@ -1500,8 +1511,28 @@ void Renderer::update(float deltaTime) { } } - // Handle normal zone transitions (only if not in tavern) - if (!insideTavern && zoneId != currentZoneId && zoneId != 0) { + // Handle blacksmith music (stop music when entering blacksmith, let ambience play) + if (insideBlacksmith) { + if (!inBlacksmith_) { + inBlacksmith_ = true; + LOG_INFO("Entered blacksmith - stopping music"); + musicManager->stopMusic(); + } + } else if (inBlacksmith_) { + // Exited blacksmith - restore zone music with crossfade + inBlacksmith_ = false; + LOG_INFO("Exited blacksmith - restoring music"); + auto* info = zoneManager->getZoneInfo(currentZoneId); + if (info) { + std::string music = zoneManager->getRandomMusic(currentZoneId); + if (!music.empty()) { + musicManager->crossfadeTo(music); + } + } + } + + // Handle normal zone transitions (only if not in tavern or blacksmith) + if (!insideTavern && !insideBlacksmith && zoneId != currentZoneId && zoneId != 0) { currentZoneId = zoneId; auto* info = zoneManager->getZoneInfo(zoneId); if (info) {