mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 09:33:51 +00:00
Add mount idle fidget animations and ambient sounds
Implements WoW-style mount idle behavior when player is stationary: - Fidget animations: discovered via property search (non-looping, 500-1500ms, stationary, IDs 1-10) - Triggers random fidget every 6-12 seconds when standing still - Ambient idle sounds: snorts/breaths for ground mounts, soft wing sounds for flyers - Triggers random idle sound every 8-15 seconds when stationary - Both systems reset timers on movement to avoid triggering while riding
This commit is contained in:
parent
4b9a2394d5
commit
8d8e780607
4 changed files with 74 additions and 1 deletions
|
|
@ -54,6 +54,7 @@ public:
|
||||||
void playRearUpSound(); // Rear-up flourish (whinny/roar)
|
void playRearUpSound(); // Rear-up flourish (whinny/roar)
|
||||||
void playJumpSound(); // Jump start (grunt/snort)
|
void playJumpSound(); // Jump start (grunt/snort)
|
||||||
void playLandSound(); // Landing (thud/hoof)
|
void playLandSound(); // Landing (thud/hoof)
|
||||||
|
void playIdleSound(); // Ambient idle (snort/stomp/breath)
|
||||||
|
|
||||||
bool isMounted() const { return mounted_; }
|
bool isMounted() const { return mounted_; }
|
||||||
void setVolumeScale(float scale) { volumeScale_ = scale; }
|
void setVolumeScale(float scale) { volumeScale_ = scale; }
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
|
|
@ -313,6 +314,7 @@ private:
|
||||||
uint32_t rearUp = 0; // Rear-up / special flourish
|
uint32_t rearUp = 0; // Rear-up / special flourish
|
||||||
uint32_t run = 0; // Run animation (discovered, don't assume)
|
uint32_t run = 0; // Run animation (discovered, don't assume)
|
||||||
uint32_t stand = 0; // Stand animation (discovered)
|
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 };
|
enum class MountAction { None, Jump, RearUp };
|
||||||
|
|
@ -326,6 +328,8 @@ private:
|
||||||
MountAction mountAction_ = MountAction::None; // Current mount action (jump/rear-up)
|
MountAction mountAction_ = MountAction::None; // Current mount action (jump/rear-up)
|
||||||
uint32_t mountActionPhase_ = 0; // 0=start, 1=loop, 2=end (for jump chaining)
|
uint32_t mountActionPhase_ = 0; // 0=start, 1=loop, 2=end (for jump chaining)
|
||||||
MountAnimSet mountAnims_; // Cached animation IDs for current mount
|
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
|
||||||
bool taxiFlight_ = false;
|
bool taxiFlight_ = false;
|
||||||
|
|
||||||
bool terrainEnabled = true;
|
bool terrainEnabled = true;
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,27 @@ void MountSoundManager::playLandSound() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MountSoundManager::playIdleSound() {
|
||||||
|
if (!mounted_ || moving_) return;
|
||||||
|
|
||||||
|
// Ambient idle sounds (snort, breath, soft neigh)
|
||||||
|
if (currentMountType_ == MountType::GROUND && !horseBreathSounds_.empty()) {
|
||||||
|
static std::mt19937 rng(std::random_device{}());
|
||||||
|
std::uniform_int_distribution<size_t> dist(0, horseBreathSounds_.size() - 1);
|
||||||
|
const auto& sample = horseBreathSounds_[dist(rng)];
|
||||||
|
if (!sample.data.empty()) {
|
||||||
|
AudioEngine::instance().playSound2D(sample.data, 0.3f * volumeScale_, 0.95f);
|
||||||
|
}
|
||||||
|
} else if (currentMountType_ == MountType::FLYING && !wingIdleSounds_.empty()) {
|
||||||
|
static std::mt19937 rng(std::random_device{}());
|
||||||
|
std::uniform_int_distribution<size_t> dist(0, wingIdleSounds_.size() - 1);
|
||||||
|
const auto& sample = wingIdleSounds_[dist(rng)];
|
||||||
|
if (!sample.data.empty()) {
|
||||||
|
AudioEngine::instance().playSound2D(sample.data, 0.25f * volumeScale_, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MountType MountSoundManager::detectMountType(uint32_t creatureDisplayId) const {
|
MountType MountSoundManager::detectMountType(uint32_t creatureDisplayId) const {
|
||||||
// TODO: Load from CreatureDisplayInfo.dbc or CreatureModelData.dbc
|
// TODO: Load from CreatureDisplayInfo.dbc or CreatureModelData.dbc
|
||||||
// For now, use simple heuristics based on common display IDs
|
// For now, use simple heuristics based on common display IDs
|
||||||
|
|
|
||||||
|
|
@ -713,6 +713,17 @@ void Renderer::setMounted(uint32_t mountInstId, uint32_t mountDisplayId, float h
|
||||||
mountAnims_.run = findFirst({5, 4}); // Run/Walk
|
mountAnims_.run = findFirst({5, 4}); // Run/Walk
|
||||||
mountAnims_.stand = findFirst({0}); // Stand (almost always 0)
|
mountAnims_.stand = findFirst({0}); // Stand (almost always 0)
|
||||||
|
|
||||||
|
// Discover idle fidget animations (head turn, tail swish, weight shift)
|
||||||
|
mountAnims_.fidgets.clear();
|
||||||
|
for (const auto& seq : sequences) {
|
||||||
|
bool isLoop = (seq.flags & 0x01) == 0;
|
||||||
|
if (!isLoop && seq.duration >= 500 && seq.duration <= 1500 &&
|
||||||
|
std::abs(seq.movingSpeed) < 0.1f && seq.id >= 1 && seq.id <= 10) {
|
||||||
|
// Likely a fidget: non-looping, short, stationary, low ID near stand
|
||||||
|
mountAnims_.fidgets.push_back(seq.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure we have fallbacks for movement
|
// Ensure we have fallbacks for movement
|
||||||
if (mountAnims_.stand == 0) mountAnims_.stand = 0; // Force 0 even if not found
|
if (mountAnims_.stand == 0) mountAnims_.stand = 0; // Force 0 even if not found
|
||||||
if (mountAnims_.run == 0) mountAnims_.run = mountAnims_.stand; // Fallback to stand if no run
|
if (mountAnims_.run == 0) mountAnims_.run = mountAnims_.stand; // Fallback to stand if no run
|
||||||
|
|
@ -722,7 +733,8 @@ void Renderer::setMounted(uint32_t mountInstId, uint32_t mountDisplayId, float h
|
||||||
" jumpEnd=", mountAnims_.jumpEnd,
|
" jumpEnd=", mountAnims_.jumpEnd,
|
||||||
" rearUp=", mountAnims_.rearUp,
|
" rearUp=", mountAnims_.rearUp,
|
||||||
" run=", mountAnims_.run,
|
" run=", mountAnims_.run,
|
||||||
" stand=", mountAnims_.stand);
|
" stand=", mountAnims_.stand,
|
||||||
|
" fidgets=", mountAnims_.fidgets.size());
|
||||||
|
|
||||||
// Notify mount sound manager
|
// Notify mount sound manager
|
||||||
if (mountSoundManager) {
|
if (mountSoundManager) {
|
||||||
|
|
@ -1029,6 +1041,41 @@ void Renderer::updateCharacterAnimation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Idle fidgets: random one-shot animations when standing still
|
||||||
|
if (!moving && mountAction_ == MountAction::None && !mountAnims_.fidgets.empty()) {
|
||||||
|
mountIdleFidgetTimer_ += lastDeltaTime_;
|
||||||
|
static float nextFidgetTime = 6.0f + (rand() % 7); // 6-12 seconds
|
||||||
|
|
||||||
|
if (mountIdleFidgetTimer_ >= nextFidgetTime) {
|
||||||
|
// Trigger random fidget animation
|
||||||
|
static std::mt19937 rng(std::random_device{}());
|
||||||
|
std::uniform_int_distribution<size_t> dist(0, mountAnims_.fidgets.size() - 1);
|
||||||
|
uint32_t fidgetAnim = mountAnims_.fidgets[dist(rng)];
|
||||||
|
|
||||||
|
characterRenderer->playAnimation(mountInstanceId_, fidgetAnim, false);
|
||||||
|
mountIdleFidgetTimer_ = 0.0f;
|
||||||
|
nextFidgetTime = 6.0f + (rand() % 7); // Randomize next fidget time
|
||||||
|
|
||||||
|
LOG_INFO("Mount idle fidget: playing anim ", fidgetAnim);
|
||||||
|
}
|
||||||
|
} else if (moving) {
|
||||||
|
mountIdleFidgetTimer_ = 0.0f; // Reset timer when moving
|
||||||
|
}
|
||||||
|
|
||||||
|
// Idle ambient sounds: random snorts/stomps/breaths when standing still
|
||||||
|
if (!moving && mountSoundManager) {
|
||||||
|
mountIdleSoundTimer_ += lastDeltaTime_;
|
||||||
|
static float nextIdleSoundTime = 8.0f + (rand() % 8); // 8-15 seconds
|
||||||
|
|
||||||
|
if (mountIdleSoundTimer_ >= nextIdleSoundTime) {
|
||||||
|
mountSoundManager->playIdleSound();
|
||||||
|
mountIdleSoundTimer_ = 0.0f;
|
||||||
|
nextIdleSoundTime = 8.0f + (rand() % 8); // Randomize next sound time
|
||||||
|
}
|
||||||
|
} else if (moving) {
|
||||||
|
mountIdleSoundTimer_ = 0.0f; // Reset timer when moving
|
||||||
|
}
|
||||||
|
|
||||||
// Only update animation if it changed and we're not in an action sequence
|
// Only update animation if it changed and we're not in an action sequence
|
||||||
if (mountAction_ == MountAction::None && (!haveMountState || curMountAnim != mountAnimId)) {
|
if (mountAction_ == MountAction::None && (!haveMountState || curMountAnim != mountAnimId)) {
|
||||||
bool loop = true; // Normal movement animations loop
|
bool loop = true; // Normal movement animations loop
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue