mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix combat vocalizations with correct MPQ paths, add combat idle stance
Use actual WoW 3.3.5a PlayerExertions and Vox sound paths from MPQ manifests for attack grunts, wounds, and death sounds. Handle Blizzard naming quirks (HumanFeamle typo, OrcMale no Final suffix, Scourge→Undead). Add COMBAT_IDLE animation state with ready weapon stance between swings. Restore deleted MPQ sound manifest docs.
This commit is contained in:
parent
8a9d9f47db
commit
328ec9ea78
5 changed files with 10623 additions and 84 deletions
5415
docs/commonsound_manifest.txt
Normal file
5415
docs/commonsound_manifest.txt
Normal file
File diff suppressed because it is too large
Load diff
5098
docs/sound manifest speech-enUS.MPQ .txt
Normal file
5098
docs/sound manifest speech-enUS.MPQ .txt
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -266,7 +266,7 @@ private:
|
||||||
float characterYaw = 0.0f;
|
float characterYaw = 0.0f;
|
||||||
|
|
||||||
// Character animation state
|
// 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 };
|
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;
|
CharAnimState charAnimState = CharAnimState::IDLE;
|
||||||
void updateCharacterAnimation();
|
void updateCharacterAnimation();
|
||||||
bool isFootstepAnimationState() const;
|
bool isFootstepAnimationState() const;
|
||||||
|
|
|
||||||
|
|
@ -137,55 +137,30 @@ void ActivitySoundManager::rebuildJumpClipsForProfile(const std::string& raceFol
|
||||||
const std::string gender = male ? "Male" : "Female";
|
const std::string gender = male ? "Male" : "Female";
|
||||||
const std::string prefix = "Sound\\Character\\" + raceFolder + "\\";
|
const std::string prefix = "Sound\\Character\\" + raceFolder + "\\";
|
||||||
const std::string stem = raceBase + gender;
|
const std::string stem = raceBase + gender;
|
||||||
const std::string genderDir = male ? "Male" : "Female";
|
|
||||||
|
// Determine PlayerExertions folder/stem (same logic as combat vocals)
|
||||||
|
std::string exertFolder = stem + "Final";
|
||||||
|
std::string exertStem = stem;
|
||||||
|
if (raceBase == "Orc" && male) exertFolder = "OrcMale";
|
||||||
|
if (raceBase == "Human" && !male) exertStem = "HumanFeamle"; // Blizzard typo
|
||||||
|
std::string exertRace = raceBase;
|
||||||
|
if (raceBase == "Scourge") { exertRace = "Undead"; exertFolder = "Undead" + gender + "Final"; exertStem = "Undead" + gender; }
|
||||||
|
const std::string exertPrefix = "Sound\\Character\\PlayerExertions\\" + exertFolder + "\\" + exertStem + "Main";
|
||||||
|
|
||||||
preloadCandidates(jumpClips, {
|
preloadCandidates(jumpClips, {
|
||||||
// Common WotLK-style variants.
|
// PlayerExertions (verified from MPQ manifest)
|
||||||
prefix + stem + "\\" + stem + "Jump01.wav",
|
exertPrefix + "Jump.wav",
|
||||||
prefix + stem + "\\" + stem + "Jump02.wav",
|
// movement_sound_manager convention (also verified working)
|
||||||
prefix + stem + "\\" + stem + "Jump03.wav",
|
prefix + stem + "Jump1.wav",
|
||||||
prefix + stem + "\\" + stem + "Exertion01.wav",
|
prefix + stem + "Land1.wav",
|
||||||
prefix + stem + "\\" + stem + "Exertion02.wav",
|
// Other common variants
|
||||||
prefix + stem + "JumpA.wav",
|
prefix + stem + "JumpA.wav",
|
||||||
prefix + stem + "JumpB.wav",
|
prefix + stem + "JumpB.wav",
|
||||||
prefix + stem + "JumpC.wav",
|
|
||||||
prefix + stem + "Jump.wav",
|
prefix + stem + "Jump.wav",
|
||||||
prefix + stem + "JumpStart.wav",
|
prefix + gender + "\\" + stem + "JumpA.wav",
|
||||||
prefix + stem + "Land.wav",
|
prefix + gender + "\\" + stem + "JumpB.wav",
|
||||||
prefix + genderDir + "\\" + stem + "JumpA.wav",
|
prefix + stem + "\\" + stem + "Jump01.wav",
|
||||||
prefix + genderDir + "\\" + stem + "JumpB.wav",
|
prefix + stem + "\\" + stem + "Jump02.wav",
|
||||||
prefix + genderDir + "\\" + stem + "JumpC.wav",
|
|
||||||
prefix + genderDir + "\\" + stem + "Jump.wav",
|
|
||||||
prefix + genderDir + "\\" + stem + "JumpStart.wav",
|
|
||||||
prefix + raceBase + "JumpA.wav",
|
|
||||||
prefix + raceBase + "JumpB.wav",
|
|
||||||
prefix + raceBase + "JumpC.wav",
|
|
||||||
prefix + raceBase + "Jump.wav",
|
|
||||||
prefix + raceBase + "\\" + stem + "JumpA.wav",
|
|
||||||
prefix + raceBase + "\\" + stem + "JumpB.wav",
|
|
||||||
prefix + raceBase + "\\" + stem + "JumpC.wav",
|
|
||||||
// Alternate folder naming in some packs.
|
|
||||||
"Sound\\Character\\" + stem + "\\" + stem + "JumpA.wav",
|
|
||||||
"Sound\\Character\\" + stem + "\\" + stem + "JumpB.wav",
|
|
||||||
"Sound\\Character\\" + stem + "\\" + stem + "Jump.wav",
|
|
||||||
// Fallback safety
|
|
||||||
"Sound\\Character\\Human\\HumanMaleJumpA.wav",
|
|
||||||
"Sound\\Character\\Human\\HumanMaleJumpB.wav",
|
|
||||||
"Sound\\Character\\Human\\HumanFemaleJumpA.wav",
|
|
||||||
"Sound\\Character\\Human\\HumanFemaleJumpB.wav",
|
|
||||||
"Sound\\Character\\Human\\Male\\HumanMaleJumpA.wav",
|
|
||||||
"Sound\\Character\\Human\\Male\\HumanMaleJumpB.wav",
|
|
||||||
"Sound\\Character\\Human\\Female\\HumanFemaleJumpA.wav",
|
|
||||||
"Sound\\Character\\Human\\Female\\HumanFemaleJumpB.wav",
|
|
||||||
"Sound\\Character\\Human\\HumanMale\\HumanMaleJump01.wav",
|
|
||||||
"Sound\\Character\\Human\\HumanMale\\HumanMaleJump02.wav",
|
|
||||||
"Sound\\Character\\Human\\HumanMale\\HumanMaleJump03.wav",
|
|
||||||
"Sound\\Character\\Human\\HumanFemale\\HumanFemaleJump01.wav",
|
|
||||||
"Sound\\Character\\Human\\HumanFemale\\HumanFemaleJump02.wav",
|
|
||||||
"Sound\\Character\\Human\\HumanFemale\\HumanFemaleJump03.wav",
|
|
||||||
"Sound\\Character\\HumanMale\\HumanMaleJumpA.wav",
|
|
||||||
"Sound\\Character\\HumanMale\\HumanMaleJumpB.wav",
|
|
||||||
"Sound\\Character\\HumanFemale\\HumanFemaleJumpA.wav",
|
|
||||||
"Sound\\Character\\HumanFemale\\HumanFemaleJumpB.wav"
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -459,63 +434,87 @@ void ActivitySoundManager::rebuildCombatVocalClipsForProfile(const std::string&
|
||||||
deathClips.clear();
|
deathClips.clear();
|
||||||
|
|
||||||
const std::string gender = male ? "Male" : "Female";
|
const std::string gender = male ? "Male" : "Female";
|
||||||
// WoW MPQ convention: Sound\Character\{Race}{Gender}PC\{Race}{Gender}PC{Type}{Letter}.wav
|
const std::string stem = raceBase + gender; // e.g. HumanFemale
|
||||||
const std::string pcStem = raceBase + gender + "PC";
|
|
||||||
const std::string pcPrefix = "Sound\\Character\\" + pcStem + "\\";
|
|
||||||
// Fallback: Sound\Character\{Race}\{Race}{Gender}{Type}{Letter}.wav
|
|
||||||
const std::string plainPrefix = "Sound\\Character\\" + raceFolder + "\\";
|
|
||||||
const std::string plainStem = raceBase + gender;
|
|
||||||
|
|
||||||
// Attack grunts (A-I covers all races)
|
// WoW 3.3.5a has two sound sources for player combat vocalizations:
|
||||||
|
//
|
||||||
|
// 1) Vox files (some races only):
|
||||||
|
// Sound\Character\{Race}\{Gender}\m{Race}{Gender}{Type}Vox{Letter}.wav
|
||||||
|
// e.g. Sound\Character\Human\Female\mHumanFemaleAttackVoxA.wav
|
||||||
|
//
|
||||||
|
// 2) PlayerExertions (all races):
|
||||||
|
// Sound\Character\PlayerExertions\{Race}{Gender}Final\{Race}{Gender}Main{Type}{Letter}.wav
|
||||||
|
// e.g. Sound\Character\PlayerExertions\HumanMaleFinal\HumanMaleMainAttackA.wav
|
||||||
|
// EXCEPTIONS:
|
||||||
|
// - OrcMale uses folder "OrcMale" (no "Final" suffix)
|
||||||
|
// - HumanFemale files have Blizzard typo: "HumanFeamle" instead of "HumanFemale"
|
||||||
|
|
||||||
|
// Determine PlayerExertions folder and file stem
|
||||||
|
std::string exertFolder = stem + "Final";
|
||||||
|
std::string exertStem = stem;
|
||||||
|
// OrcMale exception: no "Final" suffix on folder
|
||||||
|
if (raceBase == "Orc" && male) exertFolder = "OrcMale";
|
||||||
|
// HumanFemale exception: Blizzard typo "Feamle"
|
||||||
|
if (raceBase == "Human" && !male) exertStem = "HumanFeamle";
|
||||||
|
// Undead uses "Scourge" in raceBase but "Undead" in PlayerExertions
|
||||||
|
std::string exertRaceBase = raceBase;
|
||||||
|
if (raceBase == "Scourge") {
|
||||||
|
exertRaceBase = "Undead";
|
||||||
|
exertFolder = "Undead" + gender + "Final";
|
||||||
|
exertStem = "Undead" + gender;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string exertPrefix = "Sound\\Character\\PlayerExertions\\" + exertFolder + "\\" + exertStem + "Main";
|
||||||
|
const std::string voxPrefix = "Sound\\Character\\" + raceFolder + "\\" + gender + "\\m" + stem;
|
||||||
|
|
||||||
|
// Attack grunts
|
||||||
std::vector<std::string> attackPaths;
|
std::vector<std::string> attackPaths;
|
||||||
for (char c = 'A'; c <= 'I'; ++c) {
|
|
||||||
std::string s(1, c);
|
|
||||||
attackPaths.push_back(pcPrefix + pcStem + "Attack" + s + ".wav");
|
|
||||||
}
|
|
||||||
for (char c = 'A'; c <= 'I'; ++c) {
|
|
||||||
std::string s(1, c);
|
|
||||||
attackPaths.push_back(plainPrefix + plainStem + "Attack" + s + ".wav");
|
|
||||||
}
|
|
||||||
// Also try exertion sounds as attack grunts
|
|
||||||
for (char c = 'A'; c <= 'F'; ++c) {
|
for (char c = 'A'; c <= 'F'; ++c) {
|
||||||
std::string s(1, c);
|
std::string s(1, c);
|
||||||
attackPaths.push_back(pcPrefix + pcStem + "Exertion" + s + ".wav");
|
attackPaths.push_back(exertPrefix + "Attack" + s + ".wav");
|
||||||
attackPaths.push_back(plainPrefix + plainStem + "Exertion" + s + ".wav");
|
attackPaths.push_back(voxPrefix + "AttackVox" + s + ".wav");
|
||||||
}
|
}
|
||||||
preloadCandidates(attackGruntClips, attackPaths);
|
preloadCandidates(attackGruntClips, attackPaths);
|
||||||
|
|
||||||
// Wound sounds (A-H covers all races)
|
// Wound sounds
|
||||||
std::vector<std::string> woundPaths;
|
std::vector<std::string> woundPaths;
|
||||||
for (char c = 'A'; c <= 'H'; ++c) {
|
for (char c = 'A'; c <= 'F'; ++c) {
|
||||||
std::string s(1, c);
|
std::string s(1, c);
|
||||||
woundPaths.push_back(pcPrefix + pcStem + "Wound" + s + ".wav");
|
woundPaths.push_back(exertPrefix + "Wound" + s + ".wav");
|
||||||
}
|
woundPaths.push_back(voxPrefix + "WoundVox" + s + ".wav");
|
||||||
for (char c = 'A'; c <= 'H'; ++c) {
|
|
||||||
std::string s(1, c);
|
|
||||||
woundPaths.push_back(plainPrefix + plainStem + "Wound" + s + ".wav");
|
|
||||||
}
|
}
|
||||||
preloadCandidates(woundClips, woundPaths);
|
preloadCandidates(woundClips, woundPaths);
|
||||||
|
|
||||||
// Wound crit sounds (A-C)
|
// Wound crit sounds
|
||||||
std::vector<std::string> woundCritPaths;
|
std::vector<std::string> woundCritPaths;
|
||||||
for (char c = 'A'; c <= 'C'; ++c) {
|
for (char c = 'A'; c <= 'C'; ++c) {
|
||||||
std::string s(1, c);
|
std::string s(1, c);
|
||||||
woundCritPaths.push_back(pcPrefix + pcStem + "WoundCrit" + s + ".wav");
|
woundCritPaths.push_back(exertPrefix + "WoundCrit" + s + ".wav");
|
||||||
woundCritPaths.push_back(plainPrefix + plainStem + "WoundCrit" + s + ".wav");
|
woundCritPaths.push_back(voxPrefix + "WoundCriticalVox" + s + ".wav");
|
||||||
}
|
}
|
||||||
|
// Some races have WoundCrit without letter suffix
|
||||||
|
woundCritPaths.push_back(exertPrefix + "WoundCrit.wav");
|
||||||
preloadCandidates(woundCritClips, woundCritPaths);
|
preloadCandidates(woundCritClips, woundCritPaths);
|
||||||
|
|
||||||
// Death sounds
|
// Death sounds
|
||||||
preloadCandidates(deathClips, {
|
std::vector<std::string> deathPaths;
|
||||||
pcPrefix + pcStem + "Death.wav",
|
for (char c = 'A'; c <= 'C'; ++c) {
|
||||||
pcPrefix + pcStem + "Death2.wav",
|
std::string s(1, c);
|
||||||
pcPrefix + pcStem + "DeathA.wav",
|
deathPaths.push_back(exertPrefix + "Death" + s + ".wav");
|
||||||
pcPrefix + pcStem + "DeathB.wav",
|
deathPaths.push_back(voxPrefix + "DeathVox" + s + ".wav");
|
||||||
plainPrefix + plainStem + "Death.wav",
|
}
|
||||||
plainPrefix + plainStem + "Death2.wav",
|
preloadCandidates(deathClips, deathPaths);
|
||||||
plainPrefix + plainStem + "DeathA.wav",
|
|
||||||
plainPrefix + plainStem + "DeathB.wav",
|
LOG_INFO("Combat vocals for ", stem, ": attack=", attackGruntClips.size(),
|
||||||
});
|
" wound=", woundClips.size(), " woundCrit=", woundCritClips.size(),
|
||||||
|
" death=", deathClips.size());
|
||||||
|
if (!attackGruntClips.empty()) LOG_INFO(" First attack: ", attackGruntClips[0].path);
|
||||||
|
if (!woundClips.empty()) LOG_INFO(" First wound: ", woundClips[0].path);
|
||||||
|
if (attackGruntClips.empty() && woundClips.empty()) {
|
||||||
|
LOG_WARNING("No combat vocal sounds found for ", stem);
|
||||||
|
LOG_WARNING(" Tried exert prefix: ", exertPrefix);
|
||||||
|
LOG_WARNING(" Tried vox prefix: ", voxPrefix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActivitySoundManager::playAttackGrunt() {
|
void ActivitySoundManager::playAttackGrunt() {
|
||||||
|
|
|
||||||
|
|
@ -960,6 +960,9 @@ void Renderer::updateCharacterAnimation() {
|
||||||
constexpr uint32_t ANIM_SWIM_IDLE = 41; // Treading water (SwimIdle)
|
constexpr uint32_t ANIM_SWIM_IDLE = 41; // Treading water (SwimIdle)
|
||||||
constexpr uint32_t ANIM_SWIM = 42; // Swimming forward (Swim)
|
constexpr uint32_t ANIM_SWIM = 42; // Swimming forward (Swim)
|
||||||
constexpr uint32_t ANIM_MOUNT = 91; // Seated on mount
|
constexpr uint32_t ANIM_MOUNT = 91; // Seated on mount
|
||||||
|
constexpr uint32_t ANIM_READY_UNARMED = 7; // Combat ready stance (unarmed)
|
||||||
|
constexpr uint32_t ANIM_READY_1H = 8; // Combat ready stance (1H weapon)
|
||||||
|
constexpr uint32_t ANIM_READY_2H = 9; // Combat ready stance (2H weapon)
|
||||||
constexpr uint32_t ANIM_FLY_IDLE = 158; // Flying mount idle/hover
|
constexpr uint32_t ANIM_FLY_IDLE = 158; // Flying mount idle/hover
|
||||||
constexpr uint32_t ANIM_FLY_FORWARD = 159; // Flying mount forward
|
constexpr uint32_t ANIM_FLY_FORWARD = 159; // Flying mount forward
|
||||||
|
|
||||||
|
|
@ -1371,6 +1374,8 @@ void Renderer::updateCharacterAnimation() {
|
||||||
newState = CharAnimState::RUN;
|
newState = CharAnimState::RUN;
|
||||||
} else if (moving) {
|
} else if (moving) {
|
||||||
newState = CharAnimState::WALK;
|
newState = CharAnimState::WALK;
|
||||||
|
} else if (inCombat_ && grounded) {
|
||||||
|
newState = CharAnimState::COMBAT_IDLE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -1493,6 +1498,8 @@ void Renderer::updateCharacterAnimation() {
|
||||||
newState = CharAnimState::WALK;
|
newState = CharAnimState::WALK;
|
||||||
} else if (sitting) {
|
} else if (sitting) {
|
||||||
newState = CharAnimState::SIT_DOWN;
|
newState = CharAnimState::SIT_DOWN;
|
||||||
|
} else if (inCombat_) {
|
||||||
|
newState = CharAnimState::COMBAT_IDLE;
|
||||||
} else {
|
} else {
|
||||||
newState = CharAnimState::IDLE;
|
newState = CharAnimState::IDLE;
|
||||||
}
|
}
|
||||||
|
|
@ -1518,6 +1525,22 @@ void Renderer::updateCharacterAnimation() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CharAnimState::COMBAT_IDLE:
|
||||||
|
if (swim) {
|
||||||
|
newState = moving ? CharAnimState::SWIM : CharAnimState::SWIM_IDLE;
|
||||||
|
} else if (!grounded && jumping) {
|
||||||
|
newState = CharAnimState::JUMP_START;
|
||||||
|
} else if (!grounded) {
|
||||||
|
newState = CharAnimState::JUMP_MID;
|
||||||
|
} else if (moving && sprinting) {
|
||||||
|
newState = CharAnimState::RUN;
|
||||||
|
} else if (moving) {
|
||||||
|
newState = CharAnimState::WALK;
|
||||||
|
} else if (!inCombat_) {
|
||||||
|
newState = CharAnimState::IDLE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case CharAnimState::CHARGE:
|
case CharAnimState::CHARGE:
|
||||||
// Stay in CHARGE until charging_ is cleared
|
// Stay in CHARGE until charging_ is cleared
|
||||||
break;
|
break;
|
||||||
|
|
@ -1589,6 +1612,10 @@ void Renderer::updateCharacterAnimation() {
|
||||||
loop = false;
|
loop = false;
|
||||||
break;
|
break;
|
||||||
case CharAnimState::MOUNT: animId = ANIM_MOUNT; loop = true; break;
|
case CharAnimState::MOUNT: animId = ANIM_MOUNT; loop = true; break;
|
||||||
|
case CharAnimState::COMBAT_IDLE:
|
||||||
|
animId = pickFirstAvailable({ANIM_READY_1H, ANIM_READY_2H, ANIM_READY_UNARMED}, ANIM_STAND);
|
||||||
|
loop = true;
|
||||||
|
break;
|
||||||
case CharAnimState::CHARGE:
|
case CharAnimState::CHARGE:
|
||||||
animId = ANIM_RUN;
|
animId = ANIM_RUN;
|
||||||
loop = true;
|
loop = true;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue