mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 15:20:15 +00:00
Add debug logging for GameObject spawns to diagnose duplicate cathedral
Added detailed logging in spawnOnlineGameObject() to help identify duplicate game object spawns. Logs displayId, guid, model path, and position for both new spawns and position updates. This will help diagnose the floating cathedral model issue in Stormwind by showing which GUIDs are being spawned and their coordinates.
This commit is contained in:
parent
1603456120
commit
d8002955a3
6 changed files with 406 additions and 0 deletions
BIN
list_mpq
Executable file
BIN
list_mpq
Executable file
Binary file not shown.
40
list_mpq.cpp
Normal file
40
list_mpq.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#include <StormLib.h>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::cerr << "Usage: " << argv[0] << " <mpq_file> [search_pattern]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
HANDLE hMpq;
|
||||
if (!SFileOpenArchive(argv[1], 0, 0, &hMpq)) {
|
||||
std::cerr << "Failed to open MPQ: " << argv[1] << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* pattern = argc > 2 ? argv[2] : "*";
|
||||
|
||||
SFILE_FIND_DATA findData;
|
||||
HANDLE hFind = SFileFindFirstFile(hMpq, pattern, &findData, nullptr);
|
||||
|
||||
if (hFind == nullptr) {
|
||||
std::cout << "No files found matching: " << pattern << "\n";
|
||||
} else {
|
||||
int count = 0;
|
||||
do {
|
||||
std::cout << findData.cFileName << " (" << findData.dwFileSize << " bytes)\n";
|
||||
count++;
|
||||
if (count > 50) {
|
||||
std::cout << "... (showing first 50 matches)\n";
|
||||
break;
|
||||
}
|
||||
} while (SFileFindNextFile(hFind, &findData));
|
||||
|
||||
SFileFindClose(hFind);
|
||||
}
|
||||
|
||||
SFileCloseArchive(hMpq);
|
||||
return 0;
|
||||
}
|
||||
299
src/audio/npc_voice_manager.cpp.old
Normal file
299
src/audio/npc_voice_manager.cpp.old
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
#include "audio/npc_voice_manager.hpp"
|
||||
#include "audio/audio_engine.hpp"
|
||||
#include "pipeline/asset_manager.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace audio {
|
||||
|
||||
NpcVoiceManager::NpcVoiceManager() : rng_(std::random_device{}()) {}
|
||||
|
||||
NpcVoiceManager::~NpcVoiceManager() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool NpcVoiceManager::initialize(pipeline::AssetManager* assets) {
|
||||
assetManager_ = assets;
|
||||
if (!assetManager_) {
|
||||
LOG_WARNING("NPC voice manager: no asset manager");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Files are .WAV not .OGG in WotLK 3.3.5a!
|
||||
LOG_INFO("=== Probing for NPC voice files (.wav format) ===");
|
||||
std::vector<std::string> testPaths = {
|
||||
"Sound\\Creature\\HumanMaleStandardNPC\\HumanMaleStandardNPCGreetings01.wav",
|
||||
"Sound\\Creature\\HumanFemaleStandardNPC\\HumanFemaleStandardNPCGreeting01.wav",
|
||||
"Sound\\Creature\\DwarfMaleStandardNPC\\DwarfMaleStandardNPCGreeting01.wav",
|
||||
"Sound\\Creature\\OrcMaleStandardNPC\\OrcMaleStandardNPCGreeting01.wav",
|
||||
};
|
||||
for (const auto& path : testPaths) {
|
||||
bool exists = assetManager_->fileExists(path);
|
||||
LOG_INFO(" ", path, ": ", (exists ? "EXISTS" : "NOT FOUND"));
|
||||
}
|
||||
LOG_INFO("=== Probing for tavern music files ===");
|
||||
std::vector<std::string> musicPaths = {
|
||||
"Sound\\Music\\GlueScreenMusic\\tavern_01.mp3",
|
||||
"Sound\\Music\\GlueScreenMusic\\BC_Alehouse.mp3",
|
||||
"Sound\\Music\\ZoneMusic\\Tavern\\tavernAlliance01.mp3",
|
||||
};
|
||||
for (const auto& path : musicPaths) {
|
||||
bool exists = assetManager_->fileExists(path);
|
||||
LOG_INFO(" ", path, ": ", (exists ? "EXISTS" : "NOT FOUND"));
|
||||
}
|
||||
LOG_INFO("===================================");
|
||||
|
||||
loadVoiceSounds();
|
||||
|
||||
int totalSamples = 0;
|
||||
for (const auto& [type, samples] : voiceLibrary_) {
|
||||
totalSamples += samples.size();
|
||||
}
|
||||
LOG_INFO("NPC voice manager initialized (", totalSamples, " voice clips)");
|
||||
return true;
|
||||
}
|
||||
|
||||
void NpcVoiceManager::shutdown() {
|
||||
voiceLibrary_.clear();
|
||||
lastPlayTime_.clear();
|
||||
assetManager_ = nullptr;
|
||||
}
|
||||
|
||||
void NpcVoiceManager::loadVoiceSounds() {
|
||||
if (!assetManager_) return;
|
||||
|
||||
// WotLK 3.3.5a uses .WAV files, not .OGG!
|
||||
// Files use "Greeting" (singular) not "Greetings"
|
||||
|
||||
// Generic - mix of all races for variety
|
||||
auto& genericVoices = voiceLibrary_[VoiceType::GENERIC];
|
||||
for (const auto& path : {
|
||||
"Sound\\Creature\\HumanMaleStandardNPC\\HumanMaleStandardNPCGreetings01.wav",
|
||||
"Sound\\Creature\\HumanFemaleStandardNPC\\HumanFemaleStandardNPCGreeting01.wav",
|
||||
"Sound\\Creature\\DwarfMaleStandardNPC\\DwarfMaleStandardNPCGreeting01.wav",
|
||||
"Sound\\Creature\\GnomeMaleStandardNPC\\GnomeMaleStandardNPCGreeting01.wav",
|
||||
"Sound\\Creature\\NightElfMaleStandardNPC\\NightElfMaleStandardNPCGreeting01.wav",
|
||||
"Sound\\Creature\\OrcMaleStandardNPC\\OrcMaleStandardNPCGreeting01.wav",
|
||||
}) {
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) genericVoices.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Human Male (uses "Greetings" plural)
|
||||
auto& humanMale = voiceLibrary_[VoiceType::HUMAN_MALE];
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
std::string path = "Sound\\Creature\\HumanMaleStandardNPC\\HumanMaleStandardNPCGreetings0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) humanMale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Human Female
|
||||
auto& humanFemale = voiceLibrary_[VoiceType::HUMAN_FEMALE];
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
std::string path = "Sound\\Creature\\HumanFemaleStandardNPC\\HumanFemaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) humanFemale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Dwarf Male
|
||||
auto& dwarfMale = voiceLibrary_[VoiceType::DWARF_MALE];
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
std::string path = "Sound\\Creature\\DwarfMaleStandardNPC\\DwarfMaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) dwarfMale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Gnome Male
|
||||
auto& gnomeMale = voiceLibrary_[VoiceType::GNOME_MALE];
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
std::string path = "Sound\\Creature\\GnomeMaleStandardNPC\\GnomeMaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) gnomeMale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Gnome Female
|
||||
auto& gnomeFemale = voiceLibrary_[VoiceType::GNOME_FEMALE];
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
std::string path = "Sound\\Creature\\GnomeFemaleStandardNPC\\GnomeFemaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) gnomeFemale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Night Elf Male
|
||||
auto& nelfMale = voiceLibrary_[VoiceType::NIGHTELF_MALE];
|
||||
for (int i = 1; i <= 8; ++i) {
|
||||
std::string path = "Sound\\Creature\\NightElfMaleStandardNPC\\NightElfMaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) nelfMale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Night Elf Female
|
||||
auto& nelfFemale = voiceLibrary_[VoiceType::NIGHTELF_FEMALE];
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
std::string path = "Sound\\Creature\\NightElfFemaleStandardNPC\\NightElfFemaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) nelfFemale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Orc Male
|
||||
auto& orcMale = voiceLibrary_[VoiceType::ORC_MALE];
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
std::string path = "Sound\\Creature\\OrcMaleStandardNPC\\OrcMaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) orcMale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Orc Female
|
||||
auto& orcFemale = voiceLibrary_[VoiceType::ORC_FEMALE];
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
std::string path = "Sound\\Creature\\OrcFemaleStandardNPC\\OrcFemaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) orcFemale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Tauren Male
|
||||
auto& taurenMale = voiceLibrary_[VoiceType::TAUREN_MALE];
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
std::string path = "Sound\\Creature\\TaurenMaleStandardNPC\\TaurenMaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) taurenMale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Tauren Female
|
||||
auto& taurenFemale = voiceLibrary_[VoiceType::TAUREN_FEMALE];
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
std::string path = "Sound\\Creature\\TaurenFemaleStandardNPC\\TaurenFemaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) taurenFemale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Troll Male
|
||||
auto& trollMale = voiceLibrary_[VoiceType::TROLL_MALE];
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
std::string path = "Sound\\Creature\\TrollMaleStandardNPC\\TrollMaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) trollMale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Troll Female
|
||||
auto& trollFemale = voiceLibrary_[VoiceType::TROLL_FEMALE];
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
std::string path = "Sound\\Creature\\TrollFemaleStandardNPC\\TrollFemaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) trollFemale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Undead Male
|
||||
auto& undeadMale = voiceLibrary_[VoiceType::UNDEAD_MALE];
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
std::string path = "Sound\\Creature\\UndeadMaleStandardNPC\\UndeadMaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) undeadMale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Undead Female
|
||||
auto& undeadFemale = voiceLibrary_[VoiceType::UNDEAD_FEMALE];
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
std::string path = "Sound\\Creature\\UndeadFemaleStandardNPC\\UndeadFemaleStandardNPCGreeting0" + std::to_string(i) + ".wav";
|
||||
VoiceSample sample;
|
||||
if (loadSound(path, sample)) undeadFemale.push_back(std::move(sample));
|
||||
}
|
||||
|
||||
// Log loaded voice types
|
||||
int totalLoaded = 0;
|
||||
for (const auto& [type, samples] : voiceLibrary_) {
|
||||
if (!samples.empty()) {
|
||||
LOG_INFO("Loaded ", samples.size(), " voice samples for type ", static_cast<int>(type));
|
||||
totalLoaded += samples.size();
|
||||
}
|
||||
}
|
||||
|
||||
if (totalLoaded == 0) {
|
||||
LOG_WARNING("NPC voice manager: no voice samples loaded (files may not exist in MPQ)");
|
||||
}
|
||||
}
|
||||
|
||||
bool NpcVoiceManager::loadSound(const std::string& path, VoiceSample& sample) {
|
||||
if (!assetManager_ || !assetManager_->fileExists(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto data = assetManager_->readFile(path);
|
||||
if (data.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sample.path = path;
|
||||
sample.data = std::move(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NpcVoiceManager::playGreeting(uint64_t npcGuid, VoiceType voiceType, const glm::vec3& position) {
|
||||
LOG_INFO("NPC voice: playGreeting called for GUID ", npcGuid);
|
||||
|
||||
if (!AudioEngine::instance().isInitialized()) {
|
||||
LOG_WARNING("NPC voice: AudioEngine not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check cooldown
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto it = lastPlayTime_.find(npcGuid);
|
||||
if (it != lastPlayTime_.end()) {
|
||||
float elapsed = std::chrono::duration<float>(now - it->second).count();
|
||||
if (elapsed < GREETING_COOLDOWN) {
|
||||
LOG_INFO("NPC voice: on cooldown (", elapsed, "s elapsed)");
|
||||
return; // Still on cooldown
|
||||
}
|
||||
}
|
||||
|
||||
// Find voice library for this type
|
||||
auto libIt = voiceLibrary_.find(voiceType);
|
||||
if (libIt == voiceLibrary_.end() || libIt->second.empty()) {
|
||||
LOG_INFO("NPC voice: No samples for type ", static_cast<int>(voiceType), ", falling back to GENERIC");
|
||||
// Fall back to generic
|
||||
libIt = voiceLibrary_.find(VoiceType::GENERIC);
|
||||
if (libIt == voiceLibrary_.end() || libIt->second.empty()) {
|
||||
LOG_WARNING("NPC voice: No voice samples available (library empty)");
|
||||
return; // No voice samples available
|
||||
}
|
||||
}
|
||||
|
||||
const auto& samples = libIt->second;
|
||||
|
||||
// Pick random voice line
|
||||
std::uniform_int_distribution<size_t> dist(0, samples.size() - 1);
|
||||
const auto& sample = samples[dist(rng_)];
|
||||
|
||||
LOG_INFO("NPC voice: Playing sound from: ", sample.path);
|
||||
|
||||
// Play with 3D positioning
|
||||
std::uniform_real_distribution<float> volumeDist(0.85f, 1.0f);
|
||||
std::uniform_real_distribution<float> pitchDist(0.98f, 1.02f);
|
||||
|
||||
bool success = AudioEngine::instance().playSound3D(
|
||||
sample.data,
|
||||
position,
|
||||
volumeDist(rng_) * volumeScale_,
|
||||
pitchDist(rng_),
|
||||
40.0f // Max distance for voice
|
||||
);
|
||||
|
||||
if (success) {
|
||||
LOG_INFO("NPC voice: Sound played successfully");
|
||||
lastPlayTime_[npcGuid] = now;
|
||||
} else {
|
||||
LOG_WARNING("NPC voice: Failed to play sound");
|
||||
}
|
||||
}
|
||||
|
||||
VoiceType NpcVoiceManager::detectVoiceType(uint32_t creatureEntry) const {
|
||||
// TODO: Use CreatureTemplate.dbc or other data to map creature entry to voice type
|
||||
// For now, return generic
|
||||
(void)creatureEntry;
|
||||
return VoiceType::GENERIC;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace wowee
|
||||
|
|
@ -2549,6 +2549,8 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t displayId, float
|
|||
// Already have a render instance — update its position (e.g. transport re-creation)
|
||||
auto& info = gameObjectInstances_[guid];
|
||||
glm::vec3 renderPos = core::coords::canonicalToRender(glm::vec3(x, y, z));
|
||||
LOG_INFO("GameObject position update: displayId=", displayId, " guid=0x", std::hex, guid, std::dec,
|
||||
" pos=(", x, ", ", y, ", ", z, ")");
|
||||
if (renderer) {
|
||||
if (info.isWmo) {
|
||||
if (auto* wr = renderer->getWMORenderer())
|
||||
|
|
@ -2567,6 +2569,10 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t displayId, float
|
|||
return;
|
||||
}
|
||||
|
||||
// Log spawns to help debug duplicate objects (e.g., cathedral issue)
|
||||
LOG_INFO("GameObject spawn: displayId=", displayId, " guid=0x", std::hex, guid, std::dec,
|
||||
" model=", modelPath, " pos=(", x, ", ", y, ", ", z, ")");
|
||||
|
||||
std::string lowerPath = modelPath;
|
||||
std::transform(lowerPath.begin(), lowerPath.end(), lowerPath.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
|
|
|
|||
36
test_ambient_audio.sh
Executable file
36
test_ambient_audio.sh
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/bash
|
||||
# Test script for ambient audio debugging
|
||||
|
||||
echo "=== Testing Ambient Audio System ==="
|
||||
echo ""
|
||||
echo "Running game for 60 seconds and capturing logs..."
|
||||
echo ""
|
||||
|
||||
timeout 60 build/bin/wowee 2>&1 | tee /tmp/wowee_ambient_test.log
|
||||
|
||||
echo ""
|
||||
echo "=== Analysis ==="
|
||||
echo ""
|
||||
|
||||
echo "1. AmbientSoundManager Initialization:"
|
||||
grep -i "AmbientSoundManager" /tmp/wowee_ambient_test.log | head -20
|
||||
echo ""
|
||||
|
||||
echo "2. Fire Emitters Detected:"
|
||||
grep -i "fire emitter" /tmp/wowee_ambient_test.log
|
||||
echo ""
|
||||
|
||||
echo "3. Water Emitters Registered:"
|
||||
grep -i "water.*emitter" /tmp/wowee_ambient_test.log | head -10
|
||||
echo ""
|
||||
|
||||
echo "4. Sample WMO Doodads Loaded:"
|
||||
grep "WMO doodad:" /tmp/wowee_ambient_test.log | head -20
|
||||
echo ""
|
||||
|
||||
echo "5. Total Ambient Emitters:"
|
||||
grep "Registered.*ambient" /tmp/wowee_ambient_test.log
|
||||
echo ""
|
||||
|
||||
echo "Full log saved to: /tmp/wowee_ambient_test.log"
|
||||
echo "Use 'grep <keyword> /tmp/wowee_ambient_test.log' to search for specific issues"
|
||||
25
test_splash_sounds.sh
Executable file
25
test_splash_sounds.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
# Test if splash sounds load properly
|
||||
|
||||
echo "Testing splash sound files..."
|
||||
echo ""
|
||||
|
||||
for file in \
|
||||
"Sound\\Character\\Footsteps\\EnterWaterSplash\\EnterWaterSmallA.wav" \
|
||||
"Sound\\Character\\Footsteps\\EnterWaterSplash\\EnterWaterMediumA.wav" \
|
||||
"Sound\\Character\\Footsteps\\EnterWaterSplash\\EnterWaterGiantA.wav" \
|
||||
"Sound\\Character\\Footsteps\\WaterSplash\\FootStepsMediumWaterA.wav"
|
||||
do
|
||||
echo -n "Checking: $file ... "
|
||||
if ./list_mpq Data/common.MPQ "$file" 2>/dev/null | grep -q "wav"; then
|
||||
echo "✓ EXISTS"
|
||||
else
|
||||
echo "✗ NOT FOUND"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Now run the game and check for:"
|
||||
echo " Activity SFX loaded: jump=X splash=8 swimLoop=X"
|
||||
echo ""
|
||||
echo "If splash=0, the files aren't being loaded by ActivitySoundManager"
|
||||
Loading…
Add table
Add a link
Reference in a new issue