mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 09:33:51 +00:00
Fix voice gender using server data and update loading screen UI
- Use authoritative playerRace/playerGender at spawn for voice profiles instead of unreliable model name parsing - Support nonbinary gender with useFemaleModel body type fallback - Move voice setup into spawnPlayerCharacter() for all spawn paths - Remove legacy single-player default Human Male clip preloading - Make loading screen text black and move progress bar to top
This commit is contained in:
parent
efc7e65dfc
commit
f2f6ffd2cd
5 changed files with 58 additions and 15 deletions
|
|
@ -27,6 +27,7 @@ public:
|
||||||
void playLanding(FootstepSurface surface, bool hardLanding);
|
void playLanding(FootstepSurface surface, bool hardLanding);
|
||||||
void setSwimmingState(bool swimming, bool moving);
|
void setSwimmingState(bool swimming, bool moving);
|
||||||
void setCharacterVoiceProfile(const std::string& modelName);
|
void setCharacterVoiceProfile(const std::string& modelName);
|
||||||
|
void setCharacterVoiceProfile(const std::string& raceFolder, const std::string& raceBase, bool male);
|
||||||
void playWaterEnter();
|
void playWaterEnter();
|
||||||
void playWaterExit();
|
void playWaterExit();
|
||||||
void playMeleeSwing();
|
void playMeleeSwing();
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,8 @@ bool ActivitySoundManager::initialize(pipeline::AssetManager* assets) {
|
||||||
assetManager = assets;
|
assetManager = assets;
|
||||||
if (!assetManager) return false;
|
if (!assetManager) return false;
|
||||||
|
|
||||||
rebuildJumpClipsForProfile("Human", "Human", true);
|
// Voice profile clips (jump, swim, hardLand, combat vocals) are set at
|
||||||
rebuildSwimLoopClipsForProfile("Human", "Human", true);
|
// character spawn via setCharacterVoiceProfile() with the correct race/gender.
|
||||||
rebuildHardLandClipsForProfile("Human", "Human", true);
|
|
||||||
|
|
||||||
preloadCandidates(splashEnterClips, {
|
preloadCandidates(splashEnterClips, {
|
||||||
"Sound\\Character\\Footsteps\\EnterWaterSplash\\EnterWaterSmallA.wav",
|
"Sound\\Character\\Footsteps\\EnterWaterSplash\\EnterWaterSmallA.wav",
|
||||||
|
|
@ -162,6 +161,11 @@ void ActivitySoundManager::rebuildJumpClipsForProfile(const std::string& raceFol
|
||||||
prefix + stem + "\\" + stem + "Jump01.wav",
|
prefix + stem + "\\" + stem + "Jump01.wav",
|
||||||
prefix + stem + "\\" + stem + "Jump02.wav",
|
prefix + stem + "\\" + stem + "Jump02.wav",
|
||||||
});
|
});
|
||||||
|
if (jumpClips.empty()) {
|
||||||
|
LOG_WARNING("No jump clips found for ", stem, " (tried exert prefix: ", exertPrefix, ")");
|
||||||
|
} else {
|
||||||
|
LOG_INFO("Loaded ", jumpClips.size(), " jump clips for ", stem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActivitySoundManager::rebuildSwimLoopClipsForProfile(const std::string& raceFolder, const std::string& raceBase, bool male) {
|
void ActivitySoundManager::rebuildSwimLoopClipsForProfile(const std::string& raceFolder, const std::string& raceBase, bool male) {
|
||||||
|
|
@ -393,6 +397,24 @@ void ActivitySoundManager::setCharacterVoiceProfile(const std::string& modelName
|
||||||
" death clips=", deathClips.size());
|
" death clips=", deathClips.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActivitySoundManager::setCharacterVoiceProfile(const std::string& raceFolder, const std::string& raceBase, bool male) {
|
||||||
|
if (!assetManager) return;
|
||||||
|
std::string key = raceFolder + "|" + raceBase + "|" + (male ? "M" : "F");
|
||||||
|
if (key == voiceProfileKey) return;
|
||||||
|
voiceProfileKey = key;
|
||||||
|
rebuildJumpClipsForProfile(raceFolder, raceBase, male);
|
||||||
|
rebuildSwimLoopClipsForProfile(raceFolder, raceBase, male);
|
||||||
|
rebuildHardLandClipsForProfile(raceFolder, raceBase, male);
|
||||||
|
rebuildCombatVocalClipsForProfile(raceFolder, raceBase, male);
|
||||||
|
core::Logger::getInstance().info("Activity SFX voice profile (explicit): ", voiceProfileKey,
|
||||||
|
" jump clips=", jumpClips.size(),
|
||||||
|
" swim clips=", swimLoopClips.size(),
|
||||||
|
" hardLand clips=", hardLandClips.size(),
|
||||||
|
" attackGrunt clips=", attackGruntClips.size(),
|
||||||
|
" wound clips=", woundClips.size(),
|
||||||
|
" death clips=", deathClips.size());
|
||||||
|
}
|
||||||
|
|
||||||
void ActivitySoundManager::playWaterEnter() {
|
void ActivitySoundManager::playWaterEnter() {
|
||||||
LOG_INFO("Water entry detected - attempting to play splash sound");
|
LOG_INFO("Water entry detected - attempting to play splash sound");
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
|
|
||||||
|
|
@ -2545,6 +2545,32 @@ void Application::spawnPlayerCharacter() {
|
||||||
static_cast<int>(spawnPos.z), ")");
|
static_cast<int>(spawnPos.z), ")");
|
||||||
playerCharacterSpawned = true;
|
playerCharacterSpawned = true;
|
||||||
|
|
||||||
|
// Set voice profile to match character race/gender
|
||||||
|
if (auto* asm_ = renderer->getActivitySoundManager()) {
|
||||||
|
const char* raceFolder = "Human";
|
||||||
|
const char* raceBase = "Human";
|
||||||
|
switch (playerRace_) {
|
||||||
|
case game::Race::HUMAN: raceFolder = "Human"; raceBase = "Human"; break;
|
||||||
|
case game::Race::ORC: raceFolder = "Orc"; raceBase = "Orc"; break;
|
||||||
|
case game::Race::DWARF: raceFolder = "Dwarf"; raceBase = "Dwarf"; break;
|
||||||
|
case game::Race::NIGHT_ELF: raceFolder = "NightElf"; raceBase = "NightElf"; break;
|
||||||
|
case game::Race::UNDEAD: raceFolder = "Scourge"; raceBase = "Scourge"; break;
|
||||||
|
case game::Race::TAUREN: raceFolder = "Tauren"; raceBase = "Tauren"; break;
|
||||||
|
case game::Race::GNOME: raceFolder = "Gnome"; raceBase = "Gnome"; break;
|
||||||
|
case game::Race::TROLL: raceFolder = "Troll"; raceBase = "Troll"; break;
|
||||||
|
case game::Race::BLOOD_ELF: raceFolder = "BloodElf"; raceBase = "BloodElf"; break;
|
||||||
|
case game::Race::DRAENEI: raceFolder = "Draenei"; raceBase = "Draenei"; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
bool useFemaleVoice = (playerGender_ == game::Gender::FEMALE);
|
||||||
|
if (playerGender_ == game::Gender::NONBINARY && gameHandler) {
|
||||||
|
if (const game::Character* ch = gameHandler->getActiveCharacter()) {
|
||||||
|
useFemaleVoice = ch->useFemaleModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asm_->setCharacterVoiceProfile(std::string(raceFolder), std::string(raceBase), !useFemaleVoice);
|
||||||
|
}
|
||||||
|
|
||||||
// Track which character's appearance this instance represents so we can
|
// Track which character's appearance this instance represents so we can
|
||||||
// respawn if the user logs into a different character without restarting.
|
// respawn if the user logs into a different character without restarting.
|
||||||
spawnedPlayerGuid_ = gameHandler ? gameHandler->getActiveCharacterGuid() : 0;
|
spawnedPlayerGuid_ = gameHandler ? gameHandler->getActiveCharacterGuid() : 0;
|
||||||
|
|
|
||||||
|
|
@ -271,11 +271,11 @@ void LoadingScreen::render() {
|
||||||
ImVec2(0, 0), ImVec2(screenW, screenH));
|
ImVec2(0, 0), ImVec2(screenW, screenH));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Progress bar
|
// Progress bar (top of screen)
|
||||||
{
|
{
|
||||||
const float barWidthFrac = 0.6f;
|
const float barWidthFrac = 0.6f;
|
||||||
const float barHeight = 6.0f;
|
const float barHeight = 6.0f;
|
||||||
const float barY = screenH * 0.91f;
|
const float barY = screenH * 0.06f;
|
||||||
float barX = screenW * (0.5f - barWidthFrac * 0.5f);
|
float barX = screenW * (0.5f - barWidthFrac * 0.5f);
|
||||||
float barW = screenW * barWidthFrac;
|
float barW = screenW * barWidthFrac;
|
||||||
|
|
||||||
|
|
@ -306,20 +306,20 @@ void LoadingScreen::render() {
|
||||||
{
|
{
|
||||||
char pctBuf[32];
|
char pctBuf[32];
|
||||||
snprintf(pctBuf, sizeof(pctBuf), "%d%%", static_cast<int>(loadProgress * 100.0f));
|
snprintf(pctBuf, sizeof(pctBuf), "%d%%", static_cast<int>(loadProgress * 100.0f));
|
||||||
float barCenterY = screenH * 0.91f;
|
float barCenterY = screenH * 0.06f;
|
||||||
float textY = barCenterY - 20.0f;
|
float textY = barCenterY - 20.0f;
|
||||||
|
|
||||||
ImVec2 pctSize = ImGui::CalcTextSize(pctBuf);
|
ImVec2 pctSize = ImGui::CalcTextSize(pctBuf);
|
||||||
ImGui::SetCursorPos(ImVec2((screenW - pctSize.x) * 0.5f, textY));
|
ImGui::SetCursorPos(ImVec2((screenW - pctSize.x) * 0.5f, textY));
|
||||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "%s", pctBuf);
|
ImGui::TextColored(ImVec4(0.0f, 0.0f, 0.0f, 1.0f), "%s", pctBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status text below bar
|
// Status text below bar
|
||||||
{
|
{
|
||||||
float statusY = screenH * 0.91f + 14.0f;
|
float statusY = screenH * 0.06f + 14.0f;
|
||||||
ImVec2 statusSize = ImGui::CalcTextSize(statusText.c_str());
|
ImVec2 statusSize = ImGui::CalcTextSize(statusText.c_str());
|
||||||
ImGui::SetCursorPos(ImVec2((screenW - statusSize.x) * 0.5f, statusY));
|
ImGui::SetCursorPos(ImVec2((screenW - statusSize.x) * 0.5f, statusY));
|
||||||
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f), "%s", statusText.c_str());
|
ImGui::TextColored(ImVec4(0.0f, 0.0f, 0.0f, 1.0f), "%s", statusText.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
|
||||||
|
|
@ -2452,12 +2452,6 @@ void Renderer::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
characterRenderer->setInstancePosition(characterInstanceId, characterPosition);
|
characterRenderer->setInstancePosition(characterInstanceId, characterPosition);
|
||||||
if (activitySoundManager) {
|
|
||||||
std::string modelName;
|
|
||||||
if (characterRenderer->getInstanceModelName(characterInstanceId, modelName)) {
|
|
||||||
activitySoundManager->setCharacterVoiceProfile(modelName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Movement-facing comes from camera controller and is decoupled from LMB orbit.
|
// Movement-facing comes from camera controller and is decoupled from LMB orbit.
|
||||||
// During taxi flights, orientation is controlled by the flight path (not player input)
|
// During taxi flights, orientation is controlled by the flight path (not player input)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue