mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +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 playJumpSound(); // Jump start (grunt/snort)
|
||||
void playLandSound(); // Landing (thud/hoof)
|
||||
void playIdleSound(); // Ambient idle (snort/stomp/breath)
|
||||
|
||||
bool isMounted() const { return mounted_; }
|
||||
void setVolumeScale(float scale) { volumeScale_ = scale; }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
|
|
@ -313,6 +314,7 @@ private:
|
|||
uint32_t rearUp = 0; // Rear-up / special flourish
|
||||
uint32_t run = 0; // Run animation (discovered, don't assume)
|
||||
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 };
|
||||
|
|
@ -326,6 +328,8 @@ private:
|
|||
MountAction mountAction_ = MountAction::None; // Current mount action (jump/rear-up)
|
||||
uint32_t mountActionPhase_ = 0; // 0=start, 1=loop, 2=end (for jump chaining)
|
||||
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 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 {
|
||||
// TODO: Load from CreatureDisplayInfo.dbc or CreatureModelData.dbc
|
||||
// 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_.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
|
||||
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
|
||||
|
|
@ -722,7 +733,8 @@ void Renderer::setMounted(uint32_t mountInstId, uint32_t mountDisplayId, float h
|
|||
" jumpEnd=", mountAnims_.jumpEnd,
|
||||
" rearUp=", mountAnims_.rearUp,
|
||||
" run=", mountAnims_.run,
|
||||
" stand=", mountAnims_.stand);
|
||||
" stand=", mountAnims_.stand,
|
||||
" fidgets=", mountAnims_.fidgets.size());
|
||||
|
||||
// Notify mount sound manager
|
||||
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
|
||||
if (mountAction_ == MountAction::None && (!haveMountState || curMountAnim != mountAnimId)) {
|
||||
bool loop = true; // Normal movement animations loop
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue