chore(renderer): extract AnimationController and remove audio pass-throughs

Extract ~1,500 lines of character animation state from Renderer into a dedicated
AnimationController class, and complete the AudioCoordinator migration by removing
all 10 audio pass-through getters from Renderer.

AnimationController:
- New: include/rendering/animation_controller.hpp (182 lines)
- New: src/rendering/animation_controller.cpp (1,703 lines)
- Moves: locomotion state machine (50+ members), mount animation (40+ members),
  emote system, footstep triggering, surface detection, melee combat animations
- Renderer holds std::unique_ptr<AnimationController> and delegates completely
- AnimationController accesses audio via renderer_->getAudioCoordinator()

Audio caller migration:
- Migrate ~60 external callers from renderer->getXManager() to AudioCoordinator
  directly, grouped by access pattern:
  - UIServices: settings_panel, game_screen, toast_manager, chat_panel,
    combat_ui, window_manager
  - GameServices: game_handler, spell_handler, inventory_handler, quest_handler,
    social_handler, combat_handler
  - Application singleton: application.cpp, auth_screen.cpp, lua_engine.cpp
- Remove 10 pass-through getter definitions from renderer.cpp
- Remove 10 pass-through getter declarations from renderer.hpp
- Remove individual audio manager forward declarations from renderer.hpp
- Redirect 69 internal renderer.cpp audio calls to audioCoordinator_ directly
- game_handler.cpp: withSoundManager template uses services_.audioCoordinator;
  MFP changed from &Renderer::getUiSoundManager to &AudioCoordinator::getUiSoundManager
- GameServices struct: add AudioCoordinator* audioCoordinator member
- settings_panel: applyAudioVolumes(Renderer*) -> applyAudioVolumes(AudioCoordinator*)
This commit is contained in:
Paul 2026-04-02 13:06:31 +03:00
parent 5ef600098a
commit 5af9f7aa4b
22 changed files with 2208 additions and 1958 deletions

View file

@ -613,6 +613,7 @@ set(WOWEE_SOURCES
src/rendering/charge_effect.cpp src/rendering/charge_effect.cpp
src/rendering/spell_visual_system.cpp src/rendering/spell_visual_system.cpp
src/rendering/post_process_pipeline.cpp src/rendering/post_process_pipeline.cpp
src/rendering/animation_controller.cpp
src/rendering/loading_screen.cpp src/rendering/loading_screen.cpp
# UI # UI

View file

@ -4,6 +4,7 @@
namespace wowee { namespace wowee {
namespace rendering { class Renderer; } namespace rendering { class Renderer; }
namespace pipeline { class AssetManager; } namespace pipeline { class AssetManager; }
namespace audio { class AudioCoordinator; }
namespace game { class ExpansionRegistry; } namespace game { class ExpansionRegistry; }
namespace game { namespace game {
@ -13,6 +14,7 @@ namespace game {
// Replaces hidden Application::getInstance() singleton access. // Replaces hidden Application::getInstance() singleton access.
struct GameServices { struct GameServices {
rendering::Renderer* renderer = nullptr; rendering::Renderer* renderer = nullptr;
audio::AudioCoordinator* audioCoordinator = nullptr;
pipeline::AssetManager* assetManager = nullptr; pipeline::AssetManager* assetManager = nullptr;
ExpansionRegistry* expansionRegistry = nullptr; ExpansionRegistry* expansionRegistry = nullptr;
uint32_t gryphonDisplayId = 0; uint32_t gryphonDisplayId = 0;

View file

@ -0,0 +1,182 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <glm/glm.hpp>
namespace wowee {
namespace audio { enum class FootstepSurface : uint8_t; }
namespace rendering {
class Renderer;
// ============================================================================
// AnimationController — extracted from Renderer (§4.2)
//
// Owns the character locomotion state machine, mount animation state,
// emote system, footstep triggering, surface detection, melee combat
// animation, and activity SFX transition tracking.
// ============================================================================
class AnimationController {
public:
AnimationController();
~AnimationController();
void initialize(Renderer* renderer);
// ── Per-frame update hooks (called from Renderer::update) ──────────────
// Runs the character animation state machine (mounted + unmounted).
void updateCharacterAnimation();
// Processes animation-driven footstep events (player + mount).
void updateFootsteps(float deltaTime);
// Tracks state transitions for activity SFX (jump, landing, swim) and
// mount ambient sounds.
void updateSfxState(float deltaTime);
// Decrements melee swing timer / cooldown.
void updateMeleeTimers(float deltaTime);
// Store per-frame delta time (used inside animation state machine).
void setDeltaTime(float dt) { lastDeltaTime_ = dt; }
// ── Character follow ───────────────────────────────────────────────────
void onCharacterFollow(uint32_t instanceId);
// ── Emote support ──────────────────────────────────────────────────────
void playEmote(const std::string& emoteName);
void cancelEmote();
bool isEmoteActive() const { return emoteActive_; }
static std::string getEmoteText(const std::string& emoteName,
const std::string* targetName = nullptr);
static uint32_t getEmoteDbcId(const std::string& emoteName);
static std::string getEmoteTextByDbcId(uint32_t dbcId,
const std::string& senderName,
const std::string* targetName = nullptr);
static uint32_t getEmoteAnimByDbcId(uint32_t dbcId);
// ── Targeting / combat ─────────────────────────────────────────────────
void setTargetPosition(const glm::vec3* pos);
void setInCombat(bool combat) { inCombat_ = combat; }
bool isInCombat() const { return inCombat_; }
const glm::vec3* getTargetPosition() const { return targetPosition_; }
void resetCombatVisualState();
bool isMoving() const;
// ── Melee combat ───────────────────────────────────────────────────────
void triggerMeleeSwing();
void setEquippedWeaponType(uint32_t inventoryType) { equippedWeaponInvType_ = inventoryType; meleeAnimId_ = 0; }
void setCharging(bool charging) { charging_ = charging; }
bool isCharging() const { return charging_; }
// ── Effect triggers ────────────────────────────────────────────────────
void triggerLevelUpEffect(const glm::vec3& position);
void startChargeEffect(const glm::vec3& position, const glm::vec3& direction);
void emitChargeEffect(const glm::vec3& position, const glm::vec3& direction);
void stopChargeEffect();
// ── Mount ──────────────────────────────────────────────────────────────
void setMounted(uint32_t mountInstId, uint32_t mountDisplayId,
float heightOffset, const std::string& modelPath = "");
void setTaxiFlight(bool onTaxi) { taxiFlight_ = onTaxi; }
void setMountPitchRoll(float pitch, float roll) { mountPitch_ = pitch; mountRoll_ = roll; }
void clearMount();
bool isMounted() const { return mountInstanceId_ != 0; }
uint32_t getMountInstanceId() const { return mountInstanceId_; }
// ── Query helpers (used by Renderer) ───────────────────────────────────
bool isFootstepAnimationState() const;
float getMeleeSwingTimer() const { return meleeSwingTimer_; }
float getMountHeightOffset() const { return mountHeightOffset_; }
bool isTaxiFlight() const { return taxiFlight_; }
private:
Renderer* renderer_ = nullptr;
// Character animation state machine
enum class CharAnimState {
IDLE, WALK, RUN, JUMP_START, JUMP_MID, JUMP_END, SIT_DOWN, SITTING,
EMOTE, SWIM_IDLE, SWIM, MELEE_SWING, MOUNT, CHARGE, COMBAT_IDLE
};
CharAnimState charAnimState_ = CharAnimState::IDLE;
float locomotionStopGraceTimer_ = 0.0f;
bool locomotionWasSprinting_ = false;
uint32_t lastPlayerAnimRequest_ = UINT32_MAX;
bool lastPlayerAnimLoopRequest_ = true;
// Emote state
bool emoteActive_ = false;
uint32_t emoteAnimId_ = 0;
bool emoteLoop_ = false;
// Target facing
const glm::vec3* targetPosition_ = nullptr;
bool inCombat_ = false;
// Footstep event tracking (animation-driven)
uint32_t footstepLastAnimationId_ = 0;
float footstepLastNormTime_ = 0.0f;
bool footstepNormInitialized_ = false;
// Footstep surface cache (avoid expensive queries every step)
mutable audio::FootstepSurface cachedFootstepSurface_{};
mutable glm::vec3 cachedFootstepPosition_{0.0f, 0.0f, 0.0f};
mutable float cachedFootstepUpdateTimer_{999.0f};
// Mount footstep tracking (separate from player's)
uint32_t mountFootstepLastAnimId_ = 0;
float mountFootstepLastNormTime_ = 0.0f;
bool mountFootstepNormInitialized_ = false;
// SFX transition state
bool sfxStateInitialized_ = false;
bool sfxPrevGrounded_ = true;
bool sfxPrevJumping_ = false;
bool sfxPrevFalling_ = false;
bool sfxPrevSwimming_ = false;
// Melee combat
bool charging_ = false;
float meleeSwingTimer_ = 0.0f;
float meleeSwingCooldown_ = 0.0f;
float meleeAnimDurationMs_ = 0.0f;
uint32_t meleeAnimId_ = 0;
uint32_t equippedWeaponInvType_ = 0;
// Mount animation capabilities (discovered at mount time, varies per model)
struct MountAnimSet {
uint32_t jumpStart = 0; // Jump start animation
uint32_t jumpLoop = 0; // Jump airborne loop
uint32_t jumpEnd = 0; // Jump landing
uint32_t rearUp = 0; // Rear-up / special flourish
uint32_t run = 0; // Run animation (discovered, don't assume)
uint32_t stand = 0; // Stand animation (discovered)
std::vector<uint32_t> fidgets; // Idle fidget animations (head turn, tail swish, etc.)
};
enum class MountAction { None, Jump, RearUp };
uint32_t mountInstanceId_ = 0;
float mountHeightOffset_ = 0.0f;
float mountPitch_ = 0.0f; // Up/down tilt (radians)
float mountRoll_ = 0.0f; // Left/right banking (radians)
int mountSeatAttachmentId_ = -1; // -1 unknown, -2 unavailable
glm::vec3 smoothedMountSeatPos_ = glm::vec3(0.0f);
bool mountSeatSmoothingInit_ = false;
float prevMountYaw_ = 0.0f; // Previous yaw for turn rate calculation (procedural lean)
float lastDeltaTime_ = 0.0f; // Cached for use in updateCharacterAnimation()
MountAction mountAction_ = MountAction::None; // Current mount action (jump/rear-up)
uint32_t mountActionPhase_ = 0; // 0=start, 1=loop, 2=end (for jump chaining)
MountAnimSet mountAnims_; // Cached animation IDs for current mount
float mountIdleFidgetTimer_ = 0.0f; // Timer for random idle fidgets
float mountIdleSoundTimer_ = 0.0f; // Timer for ambient idle sounds
uint32_t mountActiveFidget_ = 0; // Currently playing fidget animation ID (0 = none)
bool taxiFlight_ = false;
bool taxiAnimsLogged_ = false;
// Private animation helpers
bool shouldTriggerFootstepEvent(uint32_t animationId, float animationTimeMs, float animationDurationMs);
audio::FootstepSurface resolveFootstepSurface() const;
uint32_t resolveMeleeAnimId();
};
} // namespace rendering
} // namespace wowee

View file

@ -19,7 +19,7 @@ namespace wowee {
namespace core { class Window; } namespace core { class Window; }
namespace rendering { class VkContext; } namespace rendering { class VkContext; }
namespace game { class World; class ZoneManager; class GameHandler; } namespace game { class World; class ZoneManager; class GameHandler; }
namespace audio { class AudioCoordinator; class MusicManager; class FootstepManager; class ActivitySoundManager; class MountSoundManager; class NpcVoiceManager; class AmbientSoundManager; class UiSoundManager; class CombatSoundManager; class SpellSoundManager; class MovementSoundManager; enum class FootstepSurface : uint8_t; enum class VoiceType; } namespace audio { class AudioCoordinator; }
namespace pipeline { class AssetManager; } namespace pipeline { class AssetManager; }
namespace rendering { namespace rendering {
@ -52,6 +52,10 @@ class CharacterPreview;
class AmdFsr3Runtime; class AmdFsr3Runtime;
class SpellVisualSystem; class SpellVisualSystem;
class PostProcessPipeline; class PostProcessPipeline;
class AnimationController;
class LevelUpEffect;
class ChargeEffect;
class SwimEffects;
class Renderer { class Renderer {
public: public:
@ -146,7 +150,7 @@ public:
float getCharacterYaw() const { return characterYaw; } float getCharacterYaw() const { return characterYaw; }
void setCharacterYaw(float yawDeg) { characterYaw = yawDeg; } void setCharacterYaw(float yawDeg) { characterYaw = yawDeg; }
// Emote support // Emote support — delegates to AnimationController (§4.2)
void playEmote(const std::string& emoteName); void playEmote(const std::string& emoteName);
void triggerLevelUpEffect(const glm::vec3& position); void triggerLevelUpEffect(const glm::vec3& position);
void cancelEmote(); void cancelEmote();
@ -159,31 +163,37 @@ public:
void playSpellVisual(uint32_t visualId, const glm::vec3& worldPosition, void playSpellVisual(uint32_t visualId, const glm::vec3& worldPosition,
bool useImpactKit = false); bool useImpactKit = false);
SpellVisualSystem* getSpellVisualSystem() const { return spellVisualSystem_.get(); } SpellVisualSystem* getSpellVisualSystem() const { return spellVisualSystem_.get(); }
bool isEmoteActive() const { return emoteActive; } bool isEmoteActive() const;
static std::string getEmoteText(const std::string& emoteName, const std::string* targetName = nullptr); static std::string getEmoteText(const std::string& emoteName, const std::string* targetName = nullptr);
static uint32_t getEmoteDbcId(const std::string& emoteName); static uint32_t getEmoteDbcId(const std::string& emoteName);
static std::string getEmoteTextByDbcId(uint32_t dbcId, const std::string& senderName, const std::string* targetName = nullptr); static std::string getEmoteTextByDbcId(uint32_t dbcId, const std::string& senderName, const std::string* targetName = nullptr);
static uint32_t getEmoteAnimByDbcId(uint32_t dbcId); static uint32_t getEmoteAnimByDbcId(uint32_t dbcId);
// Targeting support // Targeting support — delegates to AnimationController (§4.2)
void setTargetPosition(const glm::vec3* pos); void setTargetPosition(const glm::vec3* pos);
void setInCombat(bool combat) { inCombat_ = combat; } void setInCombat(bool combat);
void resetCombatVisualState(); void resetCombatVisualState();
bool isMoving() const; bool isMoving() const;
void triggerMeleeSwing(); void triggerMeleeSwing();
void setEquippedWeaponType(uint32_t inventoryType) { equippedWeaponInvType_ = inventoryType; meleeAnimId = 0; } void setEquippedWeaponType(uint32_t inventoryType);
void setCharging(bool charging) { charging_ = charging; } void setCharging(bool charging);
bool isCharging() const { return charging_; } bool isCharging() const;
void startChargeEffect(const glm::vec3& position, const glm::vec3& direction); void startChargeEffect(const glm::vec3& position, const glm::vec3& direction);
void emitChargeEffect(const glm::vec3& position, const glm::vec3& direction); void emitChargeEffect(const glm::vec3& position, const glm::vec3& direction);
void stopChargeEffect(); void stopChargeEffect();
// Mount rendering // Mount rendering — delegates to AnimationController (§4.2)
void setMounted(uint32_t mountInstId, uint32_t mountDisplayId, float heightOffset, const std::string& modelPath = ""); void setMounted(uint32_t mountInstId, uint32_t mountDisplayId, float heightOffset, const std::string& modelPath = "");
void setTaxiFlight(bool onTaxi) { taxiFlight_ = onTaxi; } void setTaxiFlight(bool onTaxi);
void setMountPitchRoll(float pitch, float roll) { mountPitch_ = pitch; mountRoll_ = roll; } void setMountPitchRoll(float pitch, float roll);
void clearMount(); void clearMount();
bool isMounted() const { return mountInstanceId_ != 0; } bool isMounted() const;
// AnimationController access (§4.2)
AnimationController* getAnimationController() const { return animationController_.get(); }
LevelUpEffect* getLevelUpEffect() const { return levelUpEffect.get(); }
ChargeEffect* getChargeEffect() const { return chargeEffect.get(); }
SwimEffects* getSwimEffects() const { return swimEffects.get(); }
// Selection circle for targeted entity // Selection circle for targeted entity
void setSelectionCircle(const glm::vec3& pos, float radius, const glm::vec3& color); void setSelectionCircle(const glm::vec3& pos, float radius, const glm::vec3& color);
@ -196,20 +206,9 @@ public:
double getLastTerrainRenderMs() const { return lastTerrainRenderMs; } double getLastTerrainRenderMs() const { return lastTerrainRenderMs; }
double getLastWMORenderMs() const { return lastWMORenderMs; } double getLastWMORenderMs() const { return lastWMORenderMs; }
double getLastM2RenderMs() const { return lastM2RenderMs; } double getLastM2RenderMs() const { return lastM2RenderMs; }
// Audio accessors — delegate to AudioCoordinator (owned by Application). // Audio coordinator — owned by Application, set via setAudioCoordinator().
// These pass-throughs remain until §4.2 moves animation audio out of Renderer.
void setAudioCoordinator(audio::AudioCoordinator* ac) { audioCoordinator_ = ac; } void setAudioCoordinator(audio::AudioCoordinator* ac) { audioCoordinator_ = ac; }
audio::AudioCoordinator* getAudioCoordinator() { return audioCoordinator_; } audio::AudioCoordinator* getAudioCoordinator() { return audioCoordinator_; }
audio::MusicManager* getMusicManager();
audio::FootstepManager* getFootstepManager();
audio::ActivitySoundManager* getActivitySoundManager();
audio::MountSoundManager* getMountSoundManager();
audio::NpcVoiceManager* getNpcVoiceManager();
audio::AmbientSoundManager* getAmbientSoundManager();
audio::UiSoundManager* getUiSoundManager();
audio::CombatSoundManager* getCombatSoundManager();
audio::SpellSoundManager* getSpellSoundManager();
audio::MovementSoundManager* getMovementSoundManager();
game::ZoneManager* getZoneManager() { return zoneManager.get(); } game::ZoneManager* getZoneManager() { return zoneManager.get(); }
LightingManager* getLightingManager() { return lightingManager.get(); } LightingManager* getLightingManager() { return lightingManager.get(); }
@ -243,6 +242,7 @@ private:
std::unique_ptr<WorldMap> worldMap; std::unique_ptr<WorldMap> worldMap;
std::unique_ptr<QuestMarkerRenderer> questMarkerRenderer; std::unique_ptr<QuestMarkerRenderer> questMarkerRenderer;
audio::AudioCoordinator* audioCoordinator_ = nullptr; // Owned by Application audio::AudioCoordinator* audioCoordinator_ = nullptr; // Owned by Application
std::unique_ptr<AnimationController> animationController_; // §4.2
std::unique_ptr<game::ZoneManager> zoneManager; std::unique_ptr<game::ZoneManager> zoneManager;
// Shadow mapping (Vulkan) // Shadow mapping (Vulkan)
static constexpr uint32_t SHADOW_MAP_SIZE = 4096; static constexpr uint32_t SHADOW_MAP_SIZE = 4096;
@ -340,27 +340,7 @@ private:
uint32_t characterInstanceId = 0; uint32_t characterInstanceId = 0;
float characterYaw = 0.0f; float characterYaw = 0.0f;
// Character animation state
enum class CharAnimState { IDLE, WALK, RUN, JUMP_START, JUMP_MID, JUMP_END, SIT_DOWN, SITTING, EMOTE, SWIM_IDLE, SWIM, MELEE_SWING, MOUNT, CHARGE, COMBAT_IDLE };
CharAnimState charAnimState = CharAnimState::IDLE;
float locomotionStopGraceTimer_ = 0.0f;
bool locomotionWasSprinting_ = false;
uint32_t lastPlayerAnimRequest_ = UINT32_MAX;
bool lastPlayerAnimLoopRequest_ = true;
void updateCharacterAnimation();
bool isFootstepAnimationState() const;
bool shouldTriggerFootstepEvent(uint32_t animationId, float animationTimeMs, float animationDurationMs);
audio::FootstepSurface resolveFootstepSurface() const;
uint32_t resolveMeleeAnimId();
// Emote state
bool emoteActive = false;
uint32_t emoteAnimId = 0;
bool emoteLoop = false;
// Target facing
const glm::vec3* targetPosition = nullptr;
bool inCombat_ = false;
// Selection circle rendering (Vulkan) // Selection circle rendering (Vulkan)
VkPipeline selCirclePipeline = VK_NULL_HANDLE; VkPipeline selCirclePipeline = VK_NULL_HANDLE;
@ -383,64 +363,7 @@ private:
void initOverlayPipeline(); void initOverlayPipeline();
void renderOverlay(const glm::vec4& color, VkCommandBuffer overrideCmd = VK_NULL_HANDLE); void renderOverlay(const glm::vec4& color, VkCommandBuffer overrideCmd = VK_NULL_HANDLE);
// Footstep event tracking (animation-driven)
uint32_t footstepLastAnimationId = 0;
float footstepLastNormTime = 0.0f;
bool footstepNormInitialized = false;
// Footstep surface cache (avoid expensive queries every step)
mutable audio::FootstepSurface cachedFootstepSurface{};
mutable glm::vec3 cachedFootstepPosition{0.0f, 0.0f, 0.0f};
mutable float cachedFootstepUpdateTimer{999.0f}; // Force initial query
// Mount footstep tracking (separate from player's)
uint32_t mountFootstepLastAnimId = 0;
float mountFootstepLastNormTime = 0.0f;
bool mountFootstepNormInitialized = false;
bool sfxStateInitialized = false;
bool sfxPrevGrounded = true;
bool sfxPrevJumping = false;
bool sfxPrevFalling = false;
bool sfxPrevSwimming = false;
bool charging_ = false;
float meleeSwingTimer = 0.0f;
float meleeSwingCooldown = 0.0f;
float meleeAnimDurationMs = 0.0f;
uint32_t meleeAnimId = 0;
uint32_t equippedWeaponInvType_ = 0;
// Mount state
// Mount animation capabilities (discovered at mount time, varies per model)
struct MountAnimSet {
uint32_t jumpStart = 0; // Jump start animation
uint32_t jumpLoop = 0; // Jump airborne loop
uint32_t jumpEnd = 0; // Jump landing
uint32_t rearUp = 0; // Rear-up / special flourish
uint32_t run = 0; // Run animation (discovered, don't assume)
uint32_t stand = 0; // Stand animation (discovered)
std::vector<uint32_t> fidgets; // Idle fidget animations (head turn, tail swish, etc.)
};
enum class MountAction { None, Jump, RearUp };
uint32_t mountInstanceId_ = 0;
float mountHeightOffset_ = 0.0f;
float mountPitch_ = 0.0f; // Up/down tilt (radians)
float mountRoll_ = 0.0f; // Left/right banking (radians)
int mountSeatAttachmentId_ = -1; // -1 unknown, -2 unavailable
glm::vec3 smoothedMountSeatPos_ = glm::vec3(0.0f);
bool mountSeatSmoothingInit_ = false;
float prevMountYaw_ = 0.0f; // Previous yaw for turn rate calculation (procedural lean)
float lastDeltaTime_ = 0.0f; // Cached for use in updateCharacterAnimation()
MountAction mountAction_ = MountAction::None; // Current mount action (jump/rear-up)
uint32_t mountActionPhase_ = 0; // 0=start, 1=loop, 2=end (for jump chaining)
MountAnimSet mountAnims_; // Cached animation IDs for current mount
float mountIdleFidgetTimer_ = 0.0f; // Timer for random idle fidgets
float mountIdleSoundTimer_ = 0.0f; // Timer for ambient idle sounds
uint32_t mountActiveFidget_ = 0; // Currently playing fidget animation ID (0 = none)
bool taxiFlight_ = false;
bool taxiAnimsLogged_ = false;
// Vulkan frame state // Vulkan frame state
VkContext* vkCtx = nullptr; VkContext* vkCtx = nullptr;
@ -491,6 +414,7 @@ private:
bool parallelRecordingEnabled_ = false; // set true after pools/buffers created bool parallelRecordingEnabled_ = false; // set true after pools/buffers created
bool endFrameInlineMode_ = false; // true when endFrame switched to INLINE render pass bool endFrameInlineMode_ = false; // true when endFrame switched to INLINE render pass
float lastDeltaTime_ = 0.0f; // cached for post-process pipeline
bool createSecondaryCommandResources(); bool createSecondaryCommandResources();
void destroySecondaryCommandResources(); void destroySecondaryCommandResources();
VkCommandBuffer beginSecondary(uint32_t secondaryIndex); VkCommandBuffer beginSecondary(uint32_t secondaryIndex);

View file

@ -8,6 +8,7 @@
namespace wowee { namespace wowee {
namespace rendering { class Renderer; } namespace rendering { class Renderer; }
namespace audio { class AudioCoordinator; }
namespace ui { namespace ui {
class InventoryScreen; class InventoryScreen;
@ -144,8 +145,8 @@ public:
void renderSettingsWindow(InventoryScreen& inventoryScreen, ChatPanel& chatPanel, void renderSettingsWindow(InventoryScreen& inventoryScreen, ChatPanel& chatPanel,
std::function<void()> saveCallback); std::function<void()> saveCallback);
/// Apply audio volume levels to all renderer sound managers /// Apply audio volume levels to all audio coordinator sound managers
void applyAudioVolumes(rendering::Renderer* renderer); void applyAudioVolumes(audio::AudioCoordinator* ac);
/// Return the platform-specific settings file path /// Return the platform-specific settings file path
static std::string getSettingsPath(); static std::string getSettingsPath();

View file

@ -6,6 +6,7 @@
#include "core/logger.hpp" #include "core/logger.hpp"
#include "core/application.hpp" #include "core/application.hpp"
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/ui_sound_manager.hpp" #include "audio/ui_sound_manager.hpp"
#include "game/expansion_profile.hpp" #include "game/expansion_profile.hpp"
#include <imgui.h> #include <imgui.h>
@ -1003,9 +1004,9 @@ static int lua_IsInRaid(lua_State* L) {
// PlaySound(soundId) — play a WoW UI sound by ID or name // PlaySound(soundId) — play a WoW UI sound by ID or name
static int lua_PlaySound(lua_State* L) { static int lua_PlaySound(lua_State* L) {
auto* renderer = core::Application::getInstance().getRenderer(); auto* ac = core::Application::getInstance().getAudioCoordinator();
if (!renderer) return 0; if (!ac) return 0;
auto* sfx = renderer->getUiSoundManager(); auto* sfx = ac->getUiSoundManager();
if (!sfx) return 0; if (!sfx) return 0;
// Accept numeric sound ID or string name // Accept numeric sound ID or string name

View file

@ -152,6 +152,7 @@ bool Application::initialize() {
// Populate game services — all subsystems now available // Populate game services — all subsystems now available
gameServices_.renderer = renderer.get(); gameServices_.renderer = renderer.get();
gameServices_.audioCoordinator = audioCoordinator_.get();
gameServices_.assetManager = assetManager.get(); gameServices_.assetManager = assetManager.get();
gameServices_.expansionRegistry = expansionRegistry_.get(); gameServices_.expansionRegistry = expansionRegistry_.get();
@ -1091,7 +1092,7 @@ void Application::logoutToLogin() {
} }
renderer->clearMount(); renderer->clearMount();
renderer->setCharacterFollow(0); renderer->setCharacterFollow(0);
if (auto* music = renderer->getMusicManager()) { if (auto* music = audioCoordinator_ ? audioCoordinator_->getMusicManager() : nullptr) {
music->stopMusic(0.0f); music->stopMusic(0.0f);
} }
} }
@ -2828,7 +2829,7 @@ void Application::setupUICallbacks() {
// Resolves soundId → SoundEntries.dbc → MPQ path → MusicManager. // Resolves soundId → SoundEntries.dbc → MPQ path → MusicManager.
gameHandler->setPlayMusicCallback([this](uint32_t soundId) { gameHandler->setPlayMusicCallback([this](uint32_t soundId) {
if (!assetManager || !renderer) return; if (!assetManager || !renderer) return;
auto* music = renderer->getMusicManager(); auto* music = audioCoordinator_ ? audioCoordinator_->getMusicManager() : nullptr;
if (!music) return; if (!music) return;
auto dbc = assetManager->loadDBC("SoundEntries.dbc"); auto dbc = assetManager->loadDBC("SoundEntries.dbc");
@ -3418,7 +3419,7 @@ void Application::setupUICallbacks() {
// NPC greeting callback - play voice line // NPC greeting callback - play voice line
gameHandler->setNpcGreetingCallback([this](uint64_t guid, const glm::vec3& position) { gameHandler->setNpcGreetingCallback([this](uint64_t guid, const glm::vec3& position) {
if (renderer && renderer->getNpcVoiceManager()) { if (audioCoordinator_ && audioCoordinator_->getNpcVoiceManager()) {
// Convert canonical to render coords for 3D audio // Convert canonical to render coords for 3D audio
glm::vec3 renderPos = core::coords::canonicalToRender(position); glm::vec3 renderPos = core::coords::canonicalToRender(position);
@ -3431,13 +3432,13 @@ void Application::setupUICallbacks() {
voiceType = entitySpawner_->detectVoiceTypeFromDisplayId(displayId); voiceType = entitySpawner_->detectVoiceTypeFromDisplayId(displayId);
} }
renderer->getNpcVoiceManager()->playGreeting(guid, voiceType, renderPos); audioCoordinator_->getNpcVoiceManager()->playGreeting(guid, voiceType, renderPos);
} }
}); });
// NPC farewell callback - play farewell voice line // NPC farewell callback - play farewell voice line
gameHandler->setNpcFarewellCallback([this](uint64_t guid, const glm::vec3& position) { gameHandler->setNpcFarewellCallback([this](uint64_t guid, const glm::vec3& position) {
if (renderer && renderer->getNpcVoiceManager()) { if (audioCoordinator_ && audioCoordinator_->getNpcVoiceManager()) {
glm::vec3 renderPos = core::coords::canonicalToRender(position); glm::vec3 renderPos = core::coords::canonicalToRender(position);
audio::VoiceType voiceType = audio::VoiceType::GENERIC; audio::VoiceType voiceType = audio::VoiceType::GENERIC;
@ -3448,13 +3449,13 @@ void Application::setupUICallbacks() {
voiceType = entitySpawner_->detectVoiceTypeFromDisplayId(displayId); voiceType = entitySpawner_->detectVoiceTypeFromDisplayId(displayId);
} }
renderer->getNpcVoiceManager()->playFarewell(guid, voiceType, renderPos); audioCoordinator_->getNpcVoiceManager()->playFarewell(guid, voiceType, renderPos);
} }
}); });
// NPC vendor callback - play vendor voice line // NPC vendor callback - play vendor voice line
gameHandler->setNpcVendorCallback([this](uint64_t guid, const glm::vec3& position) { gameHandler->setNpcVendorCallback([this](uint64_t guid, const glm::vec3& position) {
if (renderer && renderer->getNpcVoiceManager()) { if (audioCoordinator_ && audioCoordinator_->getNpcVoiceManager()) {
glm::vec3 renderPos = core::coords::canonicalToRender(position); glm::vec3 renderPos = core::coords::canonicalToRender(position);
audio::VoiceType voiceType = audio::VoiceType::GENERIC; audio::VoiceType voiceType = audio::VoiceType::GENERIC;
@ -3465,13 +3466,13 @@ void Application::setupUICallbacks() {
voiceType = entitySpawner_->detectVoiceTypeFromDisplayId(displayId); voiceType = entitySpawner_->detectVoiceTypeFromDisplayId(displayId);
} }
renderer->getNpcVoiceManager()->playVendor(guid, voiceType, renderPos); audioCoordinator_->getNpcVoiceManager()->playVendor(guid, voiceType, renderPos);
} }
}); });
// NPC aggro callback - play combat start voice line // NPC aggro callback - play combat start voice line
gameHandler->setNpcAggroCallback([this](uint64_t guid, const glm::vec3& position) { gameHandler->setNpcAggroCallback([this](uint64_t guid, const glm::vec3& position) {
if (renderer && renderer->getNpcVoiceManager()) { if (audioCoordinator_ && audioCoordinator_->getNpcVoiceManager()) {
glm::vec3 renderPos = core::coords::canonicalToRender(position); glm::vec3 renderPos = core::coords::canonicalToRender(position);
audio::VoiceType voiceType = audio::VoiceType::GENERIC; audio::VoiceType voiceType = audio::VoiceType::GENERIC;
@ -3482,7 +3483,7 @@ void Application::setupUICallbacks() {
voiceType = entitySpawner_->detectVoiceTypeFromDisplayId(displayId); voiceType = entitySpawner_->detectVoiceTypeFromDisplayId(displayId);
} }
renderer->getNpcVoiceManager()->playAggro(guid, voiceType, renderPos); audioCoordinator_->getNpcVoiceManager()->playAggro(guid, voiceType, renderPos);
} }
}); });
@ -3718,7 +3719,7 @@ void Application::spawnPlayerCharacter() {
playerCharacterSpawned = true; playerCharacterSpawned = true;
// Set voice profile to match character race/gender // Set voice profile to match character race/gender
if (auto* asm_ = renderer->getActivitySoundManager()) { if (auto* asm_ = audioCoordinator_ ? audioCoordinator_->getActivitySoundManager() : nullptr) {
const char* raceFolder = "Human"; const char* raceFolder = "Human";
const char* raceBase = "Human"; const char* raceBase = "Human";
switch (playerRace_) { switch (playerRace_) {

View file

@ -6,6 +6,7 @@
#include "game/update_field_table.hpp" #include "game/update_field_table.hpp"
#include "game/opcode_table.hpp" #include "game/opcode_table.hpp"
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/combat_sound_manager.hpp" #include "audio/combat_sound_manager.hpp"
#include "audio/activity_sound_manager.hpp" #include "audio/activity_sound_manager.hpp"
#include "core/application.hpp" #include "core/application.hpp"
@ -451,8 +452,8 @@ void CombatHandler::handleAttackerStateUpdate(network::Packet& packet) {
} }
// Play combat sounds via CombatSoundManager + character vocalizations // Play combat sounds via CombatSoundManager + character vocalizations
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* csm = renderer->getCombatSoundManager()) { if (auto* csm = ac->getCombatSoundManager()) {
auto weaponSize = audio::CombatSoundManager::WeaponSize::MEDIUM; auto weaponSize = audio::CombatSoundManager::WeaponSize::MEDIUM;
if (data.isMiss()) { if (data.isMiss()) {
csm->playWeaponMiss(false); csm->playWeaponMiss(false);
@ -466,7 +467,7 @@ void CombatHandler::handleAttackerStateUpdate(network::Packet& packet) {
} }
} }
// Character vocalizations // Character vocalizations
if (auto* asm_ = renderer->getActivitySoundManager()) { if (auto* asm_ = ac->getActivitySoundManager()) {
if (isPlayerAttacker && !data.isMiss() && data.victimState != 1 && data.victimState != 2) { if (isPlayerAttacker && !data.isMiss() && data.victimState != 1 && data.victimState != 2) {
asm_->playAttackGrunt(); asm_->playAttackGrunt();
} }

View file

@ -17,6 +17,7 @@
#include "game/update_field_table.hpp" #include "game/update_field_table.hpp"
#include "game/expansion_profile.hpp" #include "game/expansion_profile.hpp"
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/activity_sound_manager.hpp" #include "audio/activity_sound_manager.hpp"
#include "audio/combat_sound_manager.hpp" #include "audio/combat_sound_manager.hpp"
#include "audio/spell_sound_manager.hpp" #include "audio/spell_sound_manager.hpp"
@ -599,8 +600,8 @@ static QuestQueryRewards tryParseQuestRewards(const std::vector<uint8_t>& data,
template<typename ManagerGetter, typename Callback> template<typename ManagerGetter, typename Callback>
void GameHandler::withSoundManager(ManagerGetter getter, Callback cb) { void GameHandler::withSoundManager(ManagerGetter getter, Callback cb) {
if (auto* renderer = services_.renderer) { if (auto* ac = services_.audioCoordinator) {
if (auto* mgr = (renderer->*getter)()) cb(mgr); if (auto* mgr = (ac->*getter)()) cb(mgr);
} }
} }
@ -1198,9 +1199,9 @@ void GameHandler::updateTimers(float deltaTime) {
} }
if (!alreadyAnnounced && pendingLootMoneyAmount_ > 0) { if (!alreadyAnnounced && pendingLootMoneyAmount_ > 0) {
addSystemChatMessage("Looted: " + formatCopperAmount(pendingLootMoneyAmount_)); addSystemChatMessage("Looted: " + formatCopperAmount(pendingLootMoneyAmount_));
auto* renderer = services_.renderer; auto* ac = services_.audioCoordinator;
if (renderer) { if (ac) {
if (auto* sfx = renderer->getUiSoundManager()) { if (auto* sfx = ac->getUiSoundManager()) {
if (pendingLootMoneyAmount_ >= 10000) { if (pendingLootMoneyAmount_ >= 10000) {
sfx->playLootCoinLarge(); sfx->playLootCoinLarge();
} else { } else {
@ -1974,7 +1975,7 @@ void GameHandler::registerOpcodeHandlers() {
ping.age = 0.0f; ping.age = 0.0f;
minimapPings_.push_back(ping); minimapPings_.push_back(ping);
if (senderGuid != playerGuid) { if (senderGuid != playerGuid) {
withSoundManager(&rendering::Renderer::getUiSoundManager, [](auto* sfx) { sfx->playMinimapPing(); }); withSoundManager(&audio::AudioCoordinator::getUiSoundManager, [](auto* sfx) { sfx->playMinimapPing(); });
} }
}; };
dispatchTable_[Opcode::SMSG_ZONE_UNDER_ATTACK] = [this](network::Packet& packet) { dispatchTable_[Opcode::SMSG_ZONE_UNDER_ATTACK] = [this](network::Packet& packet) {
@ -2124,7 +2125,7 @@ void GameHandler::registerOpcodeHandlers() {
if (info && info->type == 17) { if (info && info->type == 17) {
addUIError("A fish is on your line!"); addUIError("A fish is on your line!");
addSystemChatMessage("A fish is on your line!"); addSystemChatMessage("A fish is on your line!");
withSoundManager(&rendering::Renderer::getUiSoundManager, [](auto* sfx) { sfx->playQuestUpdate(); }); withSoundManager(&audio::AudioCoordinator::getUiSoundManager, [](auto* sfx) { sfx->playQuestUpdate(); });
} }
} }
} }
@ -2350,7 +2351,7 @@ void GameHandler::registerOpcodeHandlers() {
} }
if (newLevel > oldLevel) { if (newLevel > oldLevel) {
addSystemChatMessage("You have reached level " + std::to_string(newLevel) + "!"); addSystemChatMessage("You have reached level " + std::to_string(newLevel) + "!");
withSoundManager(&rendering::Renderer::getUiSoundManager, [](auto* sfx) { sfx->playLevelUp(); }); withSoundManager(&audio::AudioCoordinator::getUiSoundManager, [](auto* sfx) { sfx->playLevelUp(); });
if (levelUpCallback_) levelUpCallback_(newLevel); if (levelUpCallback_) levelUpCallback_(newLevel);
fireAddonEvent("PLAYER_LEVEL_UP", {std::to_string(newLevel)}); fireAddonEvent("PLAYER_LEVEL_UP", {std::to_string(newLevel)});
} }

View file

@ -4,6 +4,7 @@
#include "game/entity.hpp" #include "game/entity.hpp"
#include "game/packet_parsers.hpp" #include "game/packet_parsers.hpp"
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/ui_sound_manager.hpp" #include "audio/ui_sound_manager.hpp"
#include "core/application.hpp" #include "core/application.hpp"
#include "core/logger.hpp" #include "core/logger.hpp"
@ -70,9 +71,9 @@ void InventoryHandler::registerOpcodes(DispatchTable& table) {
} }
if (!alreadyAnnounced) { if (!alreadyAnnounced) {
owner_.addSystemChatMessage("Looted: " + formatCopperAmount(amount)); owner_.addSystemChatMessage("Looted: " + formatCopperAmount(amount));
auto* renderer = owner_.services().renderer; auto* ac = owner_.services().audioCoordinator;
if (renderer) { if (ac) {
if (auto* sfx = renderer->getUiSoundManager()) { if (auto* sfx = ac->getUiSoundManager()) {
if (amount >= 10000) sfx->playLootCoinLarge(); if (amount >= 10000) sfx->playLootCoinLarge();
else sfx->playLootCoinSmall(); else sfx->playLootCoinSmall();
} }
@ -222,8 +223,8 @@ void InventoryHandler::registerOpcodes(DispatchTable& table) {
std::string msg = "Received item: " + link; std::string msg = "Received item: " + link;
if (count > 1) msg += " x" + std::to_string(count); if (count > 1) msg += " x" + std::to_string(count);
owner_.addSystemChatMessage(msg); owner_.addSystemChatMessage(msg);
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playLootItem(); sfx->playLootItem();
} }
if (owner_.addonEventCallback_) { if (owner_.addonEventCallback_) {
@ -253,8 +254,8 @@ void InventoryHandler::registerOpcodes(DispatchTable& table) {
" result=", static_cast<int>(result)); " result=", static_cast<int>(result));
if (result == 0) { if (result == 0) {
pendingSellToBuyback_.erase(itemGuid); pendingSellToBuyback_.erase(itemGuid);
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playDropOnGround(); sfx->playDropOnGround();
} }
if (owner_.addonEventCallback_) { if (owner_.addonEventCallback_) {
@ -295,8 +296,8 @@ void InventoryHandler::registerOpcodes(DispatchTable& table) {
const char* msg = (result < 7) ? sellErrors[result] : "Unknown sell error"; const char* msg = (result < 7) ? sellErrors[result] : "Unknown sell error";
owner_.addUIError(std::string("Sell failed: ") + msg); owner_.addUIError(std::string("Sell failed: ") + msg);
owner_.addSystemChatMessage(std::string("Sell failed: ") + msg); owner_.addSystemChatMessage(std::string("Sell failed: ") + msg);
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playError(); sfx->playError();
} }
LOG_WARNING("SMSG_SELL_ITEM error: ", (int)result, " (", msg, ")"); LOG_WARNING("SMSG_SELL_ITEM error: ", (int)result, " (", msg, ")");
@ -392,8 +393,8 @@ void InventoryHandler::registerOpcodes(DispatchTable& table) {
std::string msg = errMsg ? errMsg : "Inventory error (" + std::to_string(error) + ")."; std::string msg = errMsg ? errMsg : "Inventory error (" + std::to_string(error) + ").";
owner_.addUIError(msg); owner_.addUIError(msg);
owner_.addSystemChatMessage(msg); owner_.addSystemChatMessage(msg);
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playError(); sfx->playError();
} }
} }
@ -450,8 +451,8 @@ void InventoryHandler::registerOpcodes(DispatchTable& table) {
} }
owner_.addUIError(msg); owner_.addUIError(msg);
owner_.addSystemChatMessage(msg); owner_.addSystemChatMessage(msg);
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playError(); sfx->playError();
} }
} }
@ -474,8 +475,8 @@ void InventoryHandler::registerOpcodes(DispatchTable& table) {
std::string msg = "Purchased: " + buildItemLink(pendingBuyItemId_, buyQuality, itemLabel); std::string msg = "Purchased: " + buildItemLink(pendingBuyItemId_, buyQuality, itemLabel);
if (itemCount > 1) msg += " x" + std::to_string(itemCount); if (itemCount > 1) msg += " x" + std::to_string(itemCount);
owner_.addSystemChatMessage(msg); owner_.addSystemChatMessage(msg);
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playPickupBag(); sfx->playPickupBag();
} }
} }
@ -766,8 +767,8 @@ void InventoryHandler::handleLootRemoved(network::Packet& packet) {
std::string msgStr = "Looted: " + link; std::string msgStr = "Looted: " + link;
if (it->count > 1) msgStr += " x" + std::to_string(it->count); if (it->count > 1) msgStr += " x" + std::to_string(it->count);
owner_.addSystemChatMessage(msgStr); owner_.addSystemChatMessage(msgStr);
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playLootItem(); sfx->playLootItem();
} }
currentLoot_.items.erase(it); currentLoot_.items.erase(it);
@ -2382,8 +2383,8 @@ void InventoryHandler::handleItemQueryResponse(network::Packet& packet) {
std::string msg = "Received: " + link; std::string msg = "Received: " + link;
if (it->count > 1) msg += " x" + std::to_string(it->count); if (it->count > 1) msg += " x" + std::to_string(it->count);
owner_.addSystemChatMessage(msg); owner_.addSystemChatMessage(msg);
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) sfx->playLootItem(); if (auto* sfx = ac->getUiSoundManager()) sfx->playLootItem();
} }
if (owner_.itemLootCallback_) owner_.itemLootCallback_(data.entry, it->count, data.quality, itemName); if (owner_.itemLootCallback_) owner_.itemLootCallback_(data.entry, it->count, data.quality, itemName);
it = owner_.pendingItemPushNotifs_.erase(it); it = owner_.pendingItemPushNotifs_.erase(it);
@ -3149,8 +3150,8 @@ void InventoryHandler::handleTrainerBuySucceeded(network::Packet& packet) {
owner_.addSystemChatMessage("You have learned " + name + "."); owner_.addSystemChatMessage("You have learned " + name + ".");
else else
owner_.addSystemChatMessage("Spell learned."); owner_.addSystemChatMessage("Spell learned.");
if (auto* renderer = owner_.services().renderer) if (auto* ac = owner_.services().audioCoordinator)
if (auto* sfx = renderer->getUiSoundManager()) sfx->playQuestActivate(); if (auto* sfx = ac->getUiSoundManager()) sfx->playQuestActivate();
owner_.fireAddonEvent("TRAINER_UPDATE", {}); owner_.fireAddonEvent("TRAINER_UPDATE", {});
owner_.fireAddonEvent("SPELLS_CHANGED", {}); owner_.fireAddonEvent("SPELLS_CHANGED", {});
} }
@ -3171,8 +3172,8 @@ void InventoryHandler::handleTrainerBuyFailed(network::Packet& packet) {
else if (errorCode != 0) msg += " (error " + std::to_string(errorCode) + ")"; else if (errorCode != 0) msg += " (error " + std::to_string(errorCode) + ")";
owner_.addUIError(msg); owner_.addUIError(msg);
owner_.addSystemChatMessage(msg); owner_.addSystemChatMessage(msg);
if (auto* renderer = owner_.services().renderer) if (auto* ac = owner_.services().audioCoordinator)
if (auto* sfx = renderer->getUiSoundManager()) sfx->playError(); if (auto* sfx = ac->getUiSoundManager()) sfx->playError();
} }
// ============================================================ // ============================================================

View file

@ -6,6 +6,7 @@
#include "game/packet_parsers.hpp" #include "game/packet_parsers.hpp"
#include "network/world_socket.hpp" #include "network/world_socket.hpp"
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/ui_sound_manager.hpp" #include "audio/ui_sound_manager.hpp"
#include "core/application.hpp" #include "core/application.hpp"
#include "core/logger.hpp" #include "core/logger.hpp"
@ -469,8 +470,8 @@ void QuestHandler::registerOpcodes(DispatchTable& table) {
owner_.questCompleteCallback_(questId, it->title); owner_.questCompleteCallback_(questId, it->title);
} }
// Play quest-complete sound // Play quest-complete sound
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playQuestComplete(); sfx->playQuestComplete();
} }
questLog_.erase(it); questLog_.erase(it);
@ -1095,8 +1096,8 @@ void QuestHandler::acceptQuest() {
pendingQuestAcceptNpcGuids_[questId] = npcGuid; pendingQuestAcceptNpcGuids_[questId] = npcGuid;
// Play quest-accept sound // Play quest-accept sound
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playQuestActivate(); sfx->playQuestActivate();
} }

View file

@ -5,6 +5,7 @@
#include "game/packet_parsers.hpp" #include "game/packet_parsers.hpp"
#include "game/update_field_table.hpp" #include "game/update_field_table.hpp"
#include "game/opcode_table.hpp" #include "game/opcode_table.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/ui_sound_manager.hpp" #include "audio/ui_sound_manager.hpp"
#include "network/world_socket.hpp" #include "network/world_socket.hpp"
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
@ -1053,8 +1054,8 @@ void SocialHandler::handleDuelRequested(network::Packet& packet) {
} }
pendingDuelRequest_ = true; pendingDuelRequest_ = true;
owner_.addSystemChatMessage(duelChallengerName_ + " challenges you to a duel!"); owner_.addSystemChatMessage(duelChallengerName_ + " challenges you to a duel!");
if (auto* renderer = owner_.services().renderer) if (auto* ac = owner_.services().audioCoordinator)
if (auto* sfx = renderer->getUiSoundManager()) sfx->playTargetSelect(); if (auto* sfx = ac->getUiSoundManager()) sfx->playTargetSelect();
if (owner_.addonEventCallback_) owner_.addonEventCallback_("DUEL_REQUESTED", {duelChallengerName_}); if (owner_.addonEventCallback_) owner_.addonEventCallback_("DUEL_REQUESTED", {duelChallengerName_});
} }
@ -1219,8 +1220,8 @@ void SocialHandler::handleGroupInvite(network::Packet& packet) {
pendingInviterName = data.inviterName; pendingInviterName = data.inviterName;
if (!data.inviterName.empty()) if (!data.inviterName.empty())
owner_.addSystemChatMessage(data.inviterName + " has invited you to a group."); owner_.addSystemChatMessage(data.inviterName + " has invited you to a group.");
if (auto* renderer = owner_.services().renderer) if (auto* ac = owner_.services().audioCoordinator)
if (auto* sfx = renderer->getUiSoundManager()) sfx->playTargetSelect(); if (auto* sfx = ac->getUiSoundManager()) sfx->playTargetSelect();
if (owner_.addonEventCallback_) if (owner_.addonEventCallback_)
owner_.addonEventCallback_("PARTY_INVITE_REQUEST", {data.inviterName}); owner_.addonEventCallback_("PARTY_INVITE_REQUEST", {data.inviterName});
} }

View file

@ -4,6 +4,7 @@
#include "game/packet_parsers.hpp" #include "game/packet_parsers.hpp"
#include "game/entity.hpp" #include "game/entity.hpp"
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/spell_sound_manager.hpp" #include "audio/spell_sound_manager.hpp"
#include "audio/combat_sound_manager.hpp" #include "audio/combat_sound_manager.hpp"
#include "core/application.hpp" #include "core/application.hpp"
@ -795,8 +796,8 @@ void SpellHandler::handleCastFailed(network::Packet& packet) {
queuedSpellTarget_ = 0; queuedSpellTarget_ = 0;
// Stop precast sound // Stop precast sound
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* ssm = renderer->getSpellSoundManager()) { if (auto* ssm = ac->getSpellSoundManager()) {
ssm->stopPrecast(); ssm->stopPrecast();
} }
} }
@ -817,8 +818,8 @@ void SpellHandler::handleCastFailed(network::Packet& packet) {
msg.message = errMsg; msg.message = errMsg;
owner_.addLocalChatMessage(msg); owner_.addLocalChatMessage(msg);
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playError(); sfx->playError();
} }
@ -869,8 +870,8 @@ void SpellHandler::handleSpellStart(network::Packet& packet) {
// Play precast sound — skip profession/tradeskill spells // Play precast sound — skip profession/tradeskill spells
if (!owner_.isProfessionSpell(data.spellId)) { if (!owner_.isProfessionSpell(data.spellId)) {
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* ssm = renderer->getSpellSoundManager()) { if (auto* ssm = ac->getSpellSoundManager()) {
owner_.loadSpellNameCache(); owner_.loadSpellNameCache();
auto it = owner_.spellNameCache_.find(data.spellId); auto it = owner_.spellNameCache_.find(data.spellId);
auto school = (it != owner_.spellNameCache_.end() && it->second.schoolMask) auto school = (it != owner_.spellNameCache_.end() && it->second.schoolMask)
@ -907,8 +908,8 @@ void SpellHandler::handleSpellGo(network::Packet& packet) {
if (data.casterUnit == owner_.playerGuid) { if (data.casterUnit == owner_.playerGuid) {
// Play cast-complete sound // Play cast-complete sound
if (!owner_.isProfessionSpell(data.spellId)) { if (!owner_.isProfessionSpell(data.spellId)) {
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* ssm = renderer->getSpellSoundManager()) { if (auto* ssm = ac->getSpellSoundManager()) {
owner_.loadSpellNameCache(); owner_.loadSpellNameCache();
auto it = owner_.spellNameCache_.find(data.spellId); auto it = owner_.spellNameCache_.find(data.spellId);
auto school = (it != owner_.spellNameCache_.end() && it->second.schoolMask) auto school = (it != owner_.spellNameCache_.end() && it->second.schoolMask)
@ -931,8 +932,8 @@ void SpellHandler::handleSpellGo(network::Packet& packet) {
} }
if (isMeleeAbility) { if (isMeleeAbility) {
if (owner_.meleeSwingCallback_) owner_.meleeSwingCallback_(); if (owner_.meleeSwingCallback_) owner_.meleeSwingCallback_();
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* csm = renderer->getCombatSoundManager()) { if (auto* csm = ac->getCombatSoundManager()) {
csm->playWeaponSwing(audio::CombatSoundManager::WeaponSize::MEDIUM, false); csm->playWeaponSwing(audio::CombatSoundManager::WeaponSize::MEDIUM, false);
csm->playImpact(audio::CombatSoundManager::WeaponSize::MEDIUM, csm->playImpact(audio::CombatSoundManager::WeaponSize::MEDIUM,
audio::CombatSoundManager::ImpactType::FLESH, false); audio::CombatSoundManager::ImpactType::FLESH, false);
@ -990,8 +991,8 @@ void SpellHandler::handleSpellGo(network::Packet& packet) {
if (tgt == owner_.playerGuid) { targetsPlayer = true; break; } if (tgt == owner_.playerGuid) { targetsPlayer = true; break; }
} }
if (targetsPlayer) { if (targetsPlayer) {
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* ssm = renderer->getSpellSoundManager()) { if (auto* ssm = ac->getSpellSoundManager()) {
owner_.loadSpellNameCache(); owner_.loadSpellNameCache();
auto it = owner_.spellNameCache_.find(data.spellId); auto it = owner_.spellNameCache_.find(data.spellId);
auto school = (it != owner_.spellNameCache_.end() && it->second.schoolMask) auto school = (it != owner_.spellNameCache_.end() && it->second.schoolMask)
@ -1036,8 +1037,8 @@ void SpellHandler::handleSpellGo(network::Packet& packet) {
} }
if (playerIsHit || playerHitEnemy) { if (playerIsHit || playerHitEnemy) {
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* ssm = renderer->getSpellSoundManager()) { if (auto* ssm = ac->getSpellSoundManager()) {
owner_.loadSpellNameCache(); owner_.loadSpellNameCache();
auto it = owner_.spellNameCache_.find(data.spellId); auto it = owner_.spellNameCache_.find(data.spellId);
auto school = (it != owner_.spellNameCache_.end() && it->second.schoolMask) auto school = (it != owner_.spellNameCache_.end() && it->second.schoolMask)
@ -1396,8 +1397,8 @@ void SpellHandler::handleAchievementEarned(network::Packet& packet) {
owner_.earnedAchievements_.insert(achievementId); owner_.earnedAchievements_.insert(achievementId);
owner_.achievementDates_[achievementId] = earnDate; owner_.achievementDates_[achievementId] = earnDate;
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = renderer->getUiSoundManager()) if (auto* sfx = ac->getUiSoundManager())
sfx->playAchievementAlert(); sfx->playAchievementAlert();
} }
if (owner_.achievementEarnedCallback_) { if (owner_.achievementEarnedCallback_) {
@ -2350,8 +2351,8 @@ void SpellHandler::handleSpellFailure(network::Packet& packet) {
craftQueueRemaining_ = 0; craftQueueRemaining_ = 0;
queuedSpellId_ = 0; queuedSpellId_ = 0;
queuedSpellTarget_ = 0; queuedSpellTarget_ = 0;
if (auto* renderer = owner_.services().renderer) { if (auto* ac = owner_.services().audioCoordinator) {
if (auto* ssm = renderer->getSpellSoundManager()) { if (auto* ssm = ac->getSpellSoundManager()) {
ssm->stopPrecast(); ssm->stopPrecast();
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
#include "rendering/vk_context.hpp" #include "rendering/vk_context.hpp"
#include "pipeline/asset_manager.hpp" #include "pipeline/asset_manager.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/music_manager.hpp" #include "audio/music_manager.hpp"
#include "game/expansion_profile.hpp" #include "game/expansion_profile.hpp"
#include <imgui.h> #include <imgui.h>
@ -199,20 +200,20 @@ void AuthScreen::render(auth::AuthHandler& authHandler) {
} }
auto& app = core::Application::getInstance(); auto& app = core::Application::getInstance();
auto* renderer = app.getRenderer(); auto* ac = app.getAudioCoordinator();
if (!musicInitAttempted) { if (!musicInitAttempted) {
musicInitAttempted = true; musicInitAttempted = true;
auto* assets = app.getAssetManager(); auto* assets = app.getAssetManager();
if (renderer) { if (ac) {
auto* music = renderer->getMusicManager(); auto* music = ac->getMusicManager();
if (music && assets && assets->isInitialized() && !music->isInitialized()) { if (music && assets && assets->isInitialized() && !music->isInitialized()) {
music->initialize(assets); music->initialize(assets);
} }
} }
} }
// Login screen music // Login screen music
if (renderer) { if (ac) {
auto* music = renderer->getMusicManager(); auto* music = ac->getMusicManager();
if (music) { if (music) {
if (!loginMusicVolumeAdjusted_) { if (!loginMusicVolumeAdjusted_) {
savedMusicVolume_ = music->getVolume(); savedMusicVolume_ = music->getVolume();
@ -506,9 +507,9 @@ void AuthScreen::render(auth::AuthHandler& authHandler) {
void AuthScreen::stopLoginMusic() { void AuthScreen::stopLoginMusic() {
auto& app = core::Application::getInstance(); auto& app = core::Application::getInstance();
auto* renderer = app.getRenderer(); auto* ac = app.getAudioCoordinator();
if (!renderer) return; if (!ac) return;
auto* music = renderer->getMusicManager(); auto* music = ac->getMusicManager();
if (!music) return; if (!music) return;
if (musicPlaying) { if (musicPlaying) {
music->stopMusic(500.0f); music->stopMusic(500.0f);

View file

@ -11,6 +11,7 @@
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
#include "rendering/camera.hpp" #include "rendering/camera.hpp"
#include "rendering/camera_controller.hpp" #include "rendering/camera_controller.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/audio_engine.hpp" #include "audio/audio_engine.hpp"
#include "audio/ui_sound_manager.hpp" #include "audio/ui_sound_manager.hpp"
#include "pipeline/asset_manager.hpp" #include "pipeline/asset_manager.hpp"
@ -1109,8 +1110,8 @@ void ChatPanel::render(game::GameHandler& gameHandler,
std::string bodyLower = mMsg.message; std::string bodyLower = mMsg.message;
for (auto& c : bodyLower) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c))); for (auto& c : bodyLower) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
if (bodyLower.find(selfNameLower) != std::string::npos) { if (bodyLower.find(selfNameLower) != std::string::npos) {
if (auto* renderer = services_.renderer) { if (auto* ac = services_.audioCoordinator) {
if (auto* ui = renderer->getUiSoundManager()) if (auto* ui = ac->getUiSoundManager())
ui->playWhisperReceived(); ui->playWhisperReceived();
} }
break; // play at most once per scan pass break; // play at most once per scan pass

View file

@ -15,6 +15,7 @@
#include "rendering/camera.hpp" #include "rendering/camera.hpp"
#include "game/game_handler.hpp" #include "game/game_handler.hpp"
#include "pipeline/asset_manager.hpp" #include "pipeline/asset_manager.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/audio_engine.hpp" #include "audio/audio_engine.hpp"
#include "audio/ui_sound_manager.hpp" #include "audio/ui_sound_manager.hpp"
#include <imgui.h> #include <imgui.h>
@ -283,9 +284,11 @@ void CombatUI::renderRaidWarningOverlay(game::GameHandler& gameHandler) {
raidWarnEntries_.erase(raidWarnEntries_.begin()); raidWarnEntries_.erase(raidWarnEntries_.begin());
} }
// Whisper audio notification // Whisper audio notification
if (msg.type == game::ChatType::WHISPER && renderer) { if (msg.type == game::ChatType::WHISPER) {
if (auto* ui = renderer->getUiSoundManager()) if (auto* ac = services_.audioCoordinator) {
ui->playWhisperReceived(); if (auto* ui = ac->getUiSoundManager())
ui->playWhisperReceived();
}
} }
} }
raidWarnChatSeenCount_ = newCount; raidWarnChatSeenCount_ = newCount;

View file

@ -14,6 +14,7 @@
#include "rendering/character_renderer.hpp" #include "rendering/character_renderer.hpp"
#include "rendering/camera.hpp" #include "rendering/camera.hpp"
#include "rendering/camera_controller.hpp" #include "rendering/camera_controller.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/audio_engine.hpp" #include "audio/audio_engine.hpp"
#include "audio/music_manager.hpp" #include "audio/music_manager.hpp"
#include "game/zone_manager.hpp" #include "game/zone_manager.hpp"
@ -285,8 +286,8 @@ void GameScreen::render(game::GameHandler& gameHandler) {
uiErrors_.push_back({msg, 0.0f}); uiErrors_.push_back({msg, 0.0f});
if (uiErrors_.size() > 5) uiErrors_.erase(uiErrors_.begin()); if (uiErrors_.size() > 5) uiErrors_.erase(uiErrors_.begin());
// Play error sound for each new error (rate-limited by deque cap of 5) // Play error sound for each new error (rate-limited by deque cap of 5)
if (auto* r = services_.renderer) { if (auto* ac = services_.audioCoordinator) {
if (auto* sfx = r->getUiSoundManager()) sfx->playError(); if (auto* sfx = ac->getUiSoundManager()) sfx->playError();
} }
}); });
uiErrorCallbackSet_ = true; uiErrorCallbackSet_ = true;
@ -345,9 +346,9 @@ void GameScreen::render(game::GameHandler& gameHandler) {
// Apply saved volume settings once when audio managers first become available // Apply saved volume settings once when audio managers first become available
if (!settingsPanel_.volumeSettingsApplied_) { if (!settingsPanel_.volumeSettingsApplied_) {
auto* renderer = services_.renderer; auto* ac = services_.audioCoordinator;
if (renderer && renderer->getUiSoundManager()) { if (ac && ac->getUiSoundManager()) {
settingsPanel_.applyAudioVolumes(renderer); settingsPanel_.applyAudioVolumes(ac);
settingsPanel_.volumeSettingsApplied_ = true; settingsPanel_.volumeSettingsApplied_ = true;
} }
} }
@ -6525,38 +6526,38 @@ void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) {
} }
auto applyMuteState = [&]() { auto applyMuteState = [&]() {
auto* activeRenderer = services_.renderer; auto* ac = services_.audioCoordinator;
float masterScale = settingsPanel_.soundMuted_ ? 0.0f : static_cast<float>(settingsPanel_.pendingMasterVolume) / 100.0f; float masterScale = settingsPanel_.soundMuted_ ? 0.0f : static_cast<float>(settingsPanel_.pendingMasterVolume) / 100.0f;
audio::AudioEngine::instance().setMasterVolume(masterScale); audio::AudioEngine::instance().setMasterVolume(masterScale);
if (!activeRenderer) return; if (!ac) return;
if (auto* music = activeRenderer->getMusicManager()) { if (auto* music = ac->getMusicManager()) {
music->setVolume(settingsPanel_.pendingMusicVolume); music->setVolume(settingsPanel_.pendingMusicVolume);
} }
if (auto* ambient = activeRenderer->getAmbientSoundManager()) { if (auto* ambient = ac->getAmbientSoundManager()) {
ambient->setVolumeScale(settingsPanel_.pendingAmbientVolume / 100.0f); ambient->setVolumeScale(settingsPanel_.pendingAmbientVolume / 100.0f);
} }
if (auto* ui = activeRenderer->getUiSoundManager()) { if (auto* ui = ac->getUiSoundManager()) {
ui->setVolumeScale(settingsPanel_.pendingUiVolume / 100.0f); ui->setVolumeScale(settingsPanel_.pendingUiVolume / 100.0f);
} }
if (auto* combat = activeRenderer->getCombatSoundManager()) { if (auto* combat = ac->getCombatSoundManager()) {
combat->setVolumeScale(settingsPanel_.pendingCombatVolume / 100.0f); combat->setVolumeScale(settingsPanel_.pendingCombatVolume / 100.0f);
} }
if (auto* spell = activeRenderer->getSpellSoundManager()) { if (auto* spell = ac->getSpellSoundManager()) {
spell->setVolumeScale(settingsPanel_.pendingSpellVolume / 100.0f); spell->setVolumeScale(settingsPanel_.pendingSpellVolume / 100.0f);
} }
if (auto* movement = activeRenderer->getMovementSoundManager()) { if (auto* movement = ac->getMovementSoundManager()) {
movement->setVolumeScale(settingsPanel_.pendingMovementVolume / 100.0f); movement->setVolumeScale(settingsPanel_.pendingMovementVolume / 100.0f);
} }
if (auto* footstep = activeRenderer->getFootstepManager()) { if (auto* footstep = ac->getFootstepManager()) {
footstep->setVolumeScale(settingsPanel_.pendingFootstepVolume / 100.0f); footstep->setVolumeScale(settingsPanel_.pendingFootstepVolume / 100.0f);
} }
if (auto* npcVoice = activeRenderer->getNpcVoiceManager()) { if (auto* npcVoice = ac->getNpcVoiceManager()) {
npcVoice->setVolumeScale(settingsPanel_.pendingNpcVoiceVolume / 100.0f); npcVoice->setVolumeScale(settingsPanel_.pendingNpcVoiceVolume / 100.0f);
} }
if (auto* mount = activeRenderer->getMountSoundManager()) { if (auto* mount = ac->getMountSoundManager()) {
mount->setVolumeScale(settingsPanel_.pendingMountVolume / 100.0f); mount->setVolumeScale(settingsPanel_.pendingMountVolume / 100.0f);
} }
if (auto* activity = activeRenderer->getActivitySoundManager()) { if (auto* activity = ac->getActivitySoundManager()) {
activity->setVolumeScale(settingsPanel_.pendingActivityVolume / 100.0f); activity->setVolumeScale(settingsPanel_.pendingActivityVolume / 100.0f);
} }
}; };

View file

@ -17,6 +17,7 @@
#include "rendering/wmo_renderer.hpp" #include "rendering/wmo_renderer.hpp"
#include "rendering/character_renderer.hpp" #include "rendering/character_renderer.hpp"
#include "game/zone_manager.hpp" #include "game/zone_manager.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/audio_engine.hpp" #include "audio/audio_engine.hpp"
#include "audio/music_manager.hpp" #include "audio/music_manager.hpp"
#include "audio/ambient_sound_manager.hpp" #include "audio/ambient_sound_manager.hpp"
@ -439,7 +440,7 @@ ImGui::BeginChild("AudioSettings", ImVec2(0, 360), true);
// Helper lambda to apply audio settings // Helper lambda to apply audio settings
auto applyAudioSettings = [&]() { auto applyAudioSettings = [&]() {
applyAudioVolumes(renderer); applyAudioVolumes(services_.audioCoordinator);
saveCallback(); saveCallback();
}; };
@ -1227,29 +1228,29 @@ std::string SettingsPanel::getSettingsPath() {
return dir + "/settings.cfg"; return dir + "/settings.cfg";
} }
void SettingsPanel::applyAudioVolumes(rendering::Renderer* renderer) { void SettingsPanel::applyAudioVolumes(audio::AudioCoordinator* ac) {
if (!renderer) return; if (!ac) return;
float masterScale = soundMuted_ ? 0.0f : static_cast<float>(pendingMasterVolume) / 100.0f; float masterScale = soundMuted_ ? 0.0f : static_cast<float>(pendingMasterVolume) / 100.0f;
audio::AudioEngine::instance().setMasterVolume(masterScale); audio::AudioEngine::instance().setMasterVolume(masterScale);
if (auto* music = renderer->getMusicManager()) if (auto* music = ac->getMusicManager())
music->setVolume(pendingMusicVolume); music->setVolume(pendingMusicVolume);
if (auto* ambient = renderer->getAmbientSoundManager()) if (auto* ambient = ac->getAmbientSoundManager())
ambient->setVolumeScale(pendingAmbientVolume / 100.0f); ambient->setVolumeScale(pendingAmbientVolume / 100.0f);
if (auto* ui = renderer->getUiSoundManager()) if (auto* ui = ac->getUiSoundManager())
ui->setVolumeScale(pendingUiVolume / 100.0f); ui->setVolumeScale(pendingUiVolume / 100.0f);
if (auto* combat = renderer->getCombatSoundManager()) if (auto* combat = ac->getCombatSoundManager())
combat->setVolumeScale(pendingCombatVolume / 100.0f); combat->setVolumeScale(pendingCombatVolume / 100.0f);
if (auto* spell = renderer->getSpellSoundManager()) if (auto* spell = ac->getSpellSoundManager())
spell->setVolumeScale(pendingSpellVolume / 100.0f); spell->setVolumeScale(pendingSpellVolume / 100.0f);
if (auto* movement = renderer->getMovementSoundManager()) if (auto* movement = ac->getMovementSoundManager())
movement->setVolumeScale(pendingMovementVolume / 100.0f); movement->setVolumeScale(pendingMovementVolume / 100.0f);
if (auto* footstep = renderer->getFootstepManager()) if (auto* footstep = ac->getFootstepManager())
footstep->setVolumeScale(pendingFootstepVolume / 100.0f); footstep->setVolumeScale(pendingFootstepVolume / 100.0f);
if (auto* npcVoice = renderer->getNpcVoiceManager()) if (auto* npcVoice = ac->getNpcVoiceManager())
npcVoice->setVolumeScale(pendingNpcVoiceVolume / 100.0f); npcVoice->setVolumeScale(pendingNpcVoiceVolume / 100.0f);
if (auto* mount = renderer->getMountSoundManager()) if (auto* mount = ac->getMountSoundManager())
mount->setVolumeScale(pendingMountVolume / 100.0f); mount->setVolumeScale(pendingMountVolume / 100.0f);
if (auto* activity = renderer->getActivitySoundManager()) if (auto* activity = ac->getActivitySoundManager())
activity->setVolumeScale(pendingActivityVolume / 100.0f); activity->setVolumeScale(pendingActivityVolume / 100.0f);
} }

View file

@ -2,6 +2,7 @@
#include "game/game_handler.hpp" #include "game/game_handler.hpp"
#include "core/application.hpp" #include "core/application.hpp"
#include "rendering/renderer.hpp" #include "rendering/renderer.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/ui_sound_manager.hpp" #include "audio/ui_sound_manager.hpp"
#include <imgui.h> #include <imgui.h>
@ -461,11 +462,13 @@ void ToastManager::triggerDing(uint32_t newLevel, uint32_t hpDelta, uint32_t man
dingStats_[3] = intel; dingStats_[3] = intel;
dingStats_[4] = spi; dingStats_[4] = spi;
auto* renderer = services_.renderer; auto* ac = services_.audioCoordinator;
if (renderer) { if (ac) {
if (auto* sfx = renderer->getUiSoundManager()) { if (auto* sfx = ac->getUiSoundManager()) {
sfx->playLevelUp(); sfx->playLevelUp();
} }
}
if (auto* renderer = services_.renderer) {
renderer->playEmote("cheer"); renderer->playEmote("cheer");
} }
} }
@ -550,9 +553,9 @@ void ToastManager::triggerAchievementToast(uint32_t achievementId, std::string n
achievementToastTimer_ = ACHIEVEMENT_TOAST_DURATION; achievementToastTimer_ = ACHIEVEMENT_TOAST_DURATION;
// Play a UI sound if available // Play a UI sound if available
auto* renderer = services_.renderer; auto* ac = services_.audioCoordinator;
if (renderer) { if (ac) {
if (auto* sfx = renderer->getUiSoundManager()) { if (auto* sfx = ac->getUiSoundManager()) {
sfx->playAchievementAlert(); sfx->playAchievementAlert();
} }
} }

View file

@ -16,6 +16,7 @@
#include "game/game_handler.hpp" #include "game/game_handler.hpp"
#include "pipeline/asset_manager.hpp" #include "pipeline/asset_manager.hpp"
#include "pipeline/dbc_layout.hpp" #include "pipeline/dbc_layout.hpp"
#include "audio/audio_coordinator.hpp"
#include "audio/ui_sound_manager.hpp" #include "audio/ui_sound_manager.hpp"
#include "audio/music_manager.hpp" #include "audio/music_manager.hpp"
#include <imgui.h> #include <imgui.h>
@ -1701,9 +1702,9 @@ void WindowManager::renderEscapeMenu(SettingsPanel& settingsPanel) {
settingsPanel.showEscapeSettingsNotice = false; settingsPanel.showEscapeSettingsNotice = false;
} }
if (ImGui::Button("Quit", ImVec2(-1, 0))) { if (ImGui::Button("Quit", ImVec2(-1, 0))) {
auto* renderer = services_.renderer; auto* ac = services_.audioCoordinator;
if (renderer) { if (ac) {
if (auto* music = renderer->getMusicManager()) { if (auto* music = ac->getMusicManager()) {
music->stopMusic(0.0f); music->stopMusic(0.0f);
} }
} }