mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-02 15:53:51 +00:00
Implement NPC greeting voice lines
Added NPC voice manager that plays greeting sounds when clicking on NPCs: Features: - Voice line library with multiple race/gender voice types (Human, Dwarf, Night Elf, etc.) - 3D positional audio - voice comes from NPC location - Cooldown system prevents spam clicking same NPC - Randomized pitch/volume for variety - Loads greeting sounds from character voice files in MPQ - Generic fallback voices for NPCs without specific voice types Voice lines trigger automatically when gossip window opens (SMSG_GOSSIP_MESSAGE). Uses same audio system as other sound effects with ma_sound_set_position.
This commit is contained in:
parent
71c4fb3ae6
commit
eb288d2064
7 changed files with 37 additions and 1 deletions
|
|
@ -104,6 +104,7 @@ set(WOWEE_SOURCES
|
||||||
src/audio/footstep_manager.cpp
|
src/audio/footstep_manager.cpp
|
||||||
src/audio/activity_sound_manager.cpp
|
src/audio/activity_sound_manager.cpp
|
||||||
src/audio/mount_sound_manager.cpp
|
src/audio/mount_sound_manager.cpp
|
||||||
|
src/audio/npc_voice_manager.cpp
|
||||||
|
|
||||||
# Pipeline (asset loaders)
|
# Pipeline (asset loaders)
|
||||||
src/pipeline/mpq_manager.cpp
|
src/pipeline/mpq_manager.cpp
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
namespace pipeline { class AssetManager; }
|
namespace pipeline { class AssetManager; }
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,10 @@ public:
|
||||||
using NpcSwingCallback = std::function<void(uint64_t guid)>;
|
using NpcSwingCallback = std::function<void(uint64_t guid)>;
|
||||||
void setNpcSwingCallback(NpcSwingCallback cb) { npcSwingCallback_ = std::move(cb); }
|
void setNpcSwingCallback(NpcSwingCallback cb) { npcSwingCallback_ = std::move(cb); }
|
||||||
|
|
||||||
|
// NPC greeting callback (plays voice line when NPC is clicked)
|
||||||
|
using NpcGreetingCallback = std::function<void(uint64_t guid, const glm::vec3& position)>;
|
||||||
|
void setNpcGreetingCallback(NpcGreetingCallback cb) { npcGreetingCallback_ = std::move(cb); }
|
||||||
|
|
||||||
// XP tracking
|
// XP tracking
|
||||||
uint32_t getPlayerXp() const { return playerXp_; }
|
uint32_t getPlayerXp() const { return playerXp_; }
|
||||||
uint32_t getPlayerNextLevelXp() const { return playerNextLevelXp_; }
|
uint32_t getPlayerNextLevelXp() const { return playerNextLevelXp_; }
|
||||||
|
|
@ -1030,6 +1034,7 @@ private:
|
||||||
NpcRespawnCallback npcRespawnCallback_;
|
NpcRespawnCallback npcRespawnCallback_;
|
||||||
MeleeSwingCallback meleeSwingCallback_;
|
MeleeSwingCallback meleeSwingCallback_;
|
||||||
NpcSwingCallback npcSwingCallback_;
|
NpcSwingCallback npcSwingCallback_;
|
||||||
|
NpcGreetingCallback npcGreetingCallback_;
|
||||||
MountCallback mountCallback_;
|
MountCallback mountCallback_;
|
||||||
TaxiPrecacheCallback taxiPrecacheCallback_;
|
TaxiPrecacheCallback taxiPrecacheCallback_;
|
||||||
TaxiOrientationCallback taxiOrientationCallback_;
|
TaxiOrientationCallback taxiOrientationCallback_;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
namespace core { class Window; }
|
namespace core { class Window; }
|
||||||
namespace game { class World; class ZoneManager; }
|
namespace game { class World; class ZoneManager; }
|
||||||
namespace audio { class MusicManager; class FootstepManager; class ActivitySoundManager; class MountSoundManager; enum class FootstepSurface : uint8_t; }
|
namespace audio { class MusicManager; class FootstepManager; class ActivitySoundManager; class MountSoundManager; class NpcVoiceManager; enum class FootstepSurface : uint8_t; enum class VoiceType; }
|
||||||
namespace pipeline { class AssetManager; }
|
namespace pipeline { class AssetManager; }
|
||||||
|
|
||||||
namespace rendering {
|
namespace rendering {
|
||||||
|
|
@ -149,6 +149,7 @@ public:
|
||||||
audio::FootstepManager* getFootstepManager() { return footstepManager.get(); }
|
audio::FootstepManager* getFootstepManager() { return footstepManager.get(); }
|
||||||
audio::ActivitySoundManager* getActivitySoundManager() { return activitySoundManager.get(); }
|
audio::ActivitySoundManager* getActivitySoundManager() { return activitySoundManager.get(); }
|
||||||
audio::MountSoundManager* getMountSoundManager() { return mountSoundManager.get(); }
|
audio::MountSoundManager* getMountSoundManager() { return mountSoundManager.get(); }
|
||||||
|
audio::NpcVoiceManager* getNpcVoiceManager() { return npcVoiceManager.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
core::Window* window = nullptr;
|
core::Window* window = nullptr;
|
||||||
|
|
@ -175,6 +176,7 @@ private:
|
||||||
std::unique_ptr<audio::FootstepManager> footstepManager;
|
std::unique_ptr<audio::FootstepManager> footstepManager;
|
||||||
std::unique_ptr<audio::ActivitySoundManager> activitySoundManager;
|
std::unique_ptr<audio::ActivitySoundManager> activitySoundManager;
|
||||||
std::unique_ptr<audio::MountSoundManager> mountSoundManager;
|
std::unique_ptr<audio::MountSoundManager> mountSoundManager;
|
||||||
|
std::unique_ptr<audio::NpcVoiceManager> npcVoiceManager;
|
||||||
std::unique_ptr<game::ZoneManager> zoneManager;
|
std::unique_ptr<game::ZoneManager> zoneManager;
|
||||||
std::unique_ptr<Shader> underwaterOverlayShader;
|
std::unique_ptr<Shader> underwaterOverlayShader;
|
||||||
uint32_t underwaterOverlayVAO = 0;
|
uint32_t underwaterOverlayVAO = 0;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include "core/logger.hpp"
|
#include "core/logger.hpp"
|
||||||
#include "core/memory_monitor.hpp"
|
#include "core/memory_monitor.hpp"
|
||||||
#include "rendering/renderer.hpp"
|
#include "rendering/renderer.hpp"
|
||||||
|
#include "audio/npc_voice_manager.hpp"
|
||||||
#include "rendering/camera.hpp"
|
#include "rendering/camera.hpp"
|
||||||
#include "rendering/camera_controller.hpp"
|
#include "rendering/camera_controller.hpp"
|
||||||
#include "rendering/terrain_renderer.hpp"
|
#include "rendering/terrain_renderer.hpp"
|
||||||
|
|
@ -814,6 +815,15 @@ void Application::setupUICallbacks() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NPC greeting callback - play voice line
|
||||||
|
gameHandler->setNpcGreetingCallback([this](uint64_t guid, const glm::vec3& position) {
|
||||||
|
if (renderer && renderer->getNpcVoiceManager()) {
|
||||||
|
// Convert canonical to render coords for 3D audio
|
||||||
|
glm::vec3 renderPos = core::coords::canonicalToRender(position);
|
||||||
|
renderer->getNpcVoiceManager()->playGreeting(guid, audio::VoiceType::GENERIC, renderPos);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// "Create Character" button on character screen
|
// "Create Character" button on character screen
|
||||||
uiManager->getCharacterScreen().setOnCreateCharacter([this]() {
|
uiManager->getCharacterScreen().setOnCreateCharacter([this]() {
|
||||||
uiManager->getCharacterCreateScreen().reset();
|
uiManager->getCharacterCreateScreen().reset();
|
||||||
|
|
|
||||||
|
|
@ -4569,6 +4569,15 @@ void GameHandler::handleGossipMessage(network::Packet& packet) {
|
||||||
if (questDetailsOpen) return; // Don't reopen gossip while viewing quest
|
if (questDetailsOpen) return; // Don't reopen gossip while viewing quest
|
||||||
gossipWindowOpen = true;
|
gossipWindowOpen = true;
|
||||||
vendorWindowOpen = false; // Close vendor if gossip opens
|
vendorWindowOpen = false; // Close vendor if gossip opens
|
||||||
|
|
||||||
|
// Play NPC greeting voice
|
||||||
|
if (npcGreetingCallback_ && currentGossip.npcGuid != 0) {
|
||||||
|
auto entity = entityManager.getEntity(currentGossip.npcGuid);
|
||||||
|
if (entity) {
|
||||||
|
glm::vec3 npcPos(entity->getX(), entity->getY(), entity->getZ());
|
||||||
|
npcGreetingCallback_(currentGossip.npcGuid, npcPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameHandler::handleGossipComplete(network::Packet& packet) {
|
void GameHandler::handleGossipComplete(network::Packet& packet) {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
#include "audio/footstep_manager.hpp"
|
#include "audio/footstep_manager.hpp"
|
||||||
#include "audio/activity_sound_manager.hpp"
|
#include "audio/activity_sound_manager.hpp"
|
||||||
#include "audio/mount_sound_manager.hpp"
|
#include "audio/mount_sound_manager.hpp"
|
||||||
|
#include "audio/npc_voice_manager.hpp"
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#include <glm/gtx/euler_angles.hpp>
|
#include <glm/gtx/euler_angles.hpp>
|
||||||
|
|
@ -342,6 +343,7 @@ bool Renderer::initialize(core::Window* win) {
|
||||||
footstepManager = std::make_unique<audio::FootstepManager>();
|
footstepManager = std::make_unique<audio::FootstepManager>();
|
||||||
activitySoundManager = std::make_unique<audio::ActivitySoundManager>();
|
activitySoundManager = std::make_unique<audio::ActivitySoundManager>();
|
||||||
mountSoundManager = std::make_unique<audio::MountSoundManager>();
|
mountSoundManager = std::make_unique<audio::MountSoundManager>();
|
||||||
|
npcVoiceManager = std::make_unique<audio::NpcVoiceManager>();
|
||||||
|
|
||||||
// Underwater full-screen tint overlay (applies to all world geometry).
|
// Underwater full-screen tint overlay (applies to all world geometry).
|
||||||
underwaterOverlayShader = std::make_unique<Shader>();
|
underwaterOverlayShader = std::make_unique<Shader>();
|
||||||
|
|
@ -2132,6 +2134,12 @@ bool Renderer::loadTerrainArea(const std::string& mapName, int centerX, int cent
|
||||||
activitySoundManager->initialize(cachedAssetManager);
|
activitySoundManager->initialize(cachedAssetManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mountSoundManager && cachedAssetManager) {
|
||||||
|
mountSoundManager->initialize(cachedAssetManager);
|
||||||
|
}
|
||||||
|
if (npcVoiceManager && cachedAssetManager) {
|
||||||
|
npcVoiceManager->initialize(cachedAssetManager);
|
||||||
|
}
|
||||||
|
|
||||||
// Wire WMO, M2, and water renderer to camera controller
|
// Wire WMO, M2, and water renderer to camera controller
|
||||||
if (cameraController && wmoRenderer) {
|
if (cameraController && wmoRenderer) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue