anim: add flying state tracking and Fly/FlyIdle animation selection for entities

Previously the move-flags callback only tracked SWIMMING and WALKING,
so flying players/mounts always played Run(5) or Stand(0) animations
instead of Fly(61)/FlyIdle(60).

Changes:
- Add creatureFlyingState_ (mirroring creatureSwimmingState_) set by
  the FLYING flag (0x01000000) in unitMoveFlagsCallback_.
- Update animation selection: moving+flying → 61 (Fly/FlyForward),
  idle+flying → 60 (FlyIdle/hover). Flying takes priority over swim
  in the priority chain: fly > swim > walk > run.
- Clear creatureFlyingState_ on world reset.
This commit is contained in:
Kelsi 2026-03-10 11:56:50 -07:00
parent a33119c070
commit 30a65320fb
2 changed files with 16 additions and 4 deletions

View file

@ -190,6 +190,7 @@ private:
std::unordered_map<uint64_t, bool> creatureWasMoving_; // guid -> previous-frame movement state
std::unordered_map<uint64_t, bool> creatureSwimmingState_; // guid -> currently in swim mode (SWIMMING flag)
std::unordered_map<uint64_t, bool> creatureWalkingState_; // guid -> walking (WALKING flag, selects Walk(4) vs Run(5))
std::unordered_map<uint64_t, bool> creatureFlyingState_; // guid -> currently flying (FLYING flag)
std::unordered_set<uint64_t> creatureWeaponsAttached_; // guid set when NPC virtual weapons attached
std::unordered_map<uint64_t, uint8_t> creatureWeaponAttachAttempts_; // guid -> attach attempts
std::unordered_map<uint32_t, bool> modelIdIsWolfLike_; // modelId → cached wolf/worg check

View file

@ -752,6 +752,7 @@ void Application::logoutToLogin() {
creatureWasMoving_.clear();
creatureSwimmingState_.clear();
creatureWalkingState_.clear();
creatureFlyingState_.clear();
deadCreatureGuids_.clear();
nonRenderableCreatureDisplayIds_.clear();
creaturePermanentFailureGuids_.clear();
@ -1485,6 +1486,7 @@ void Application::update(float deltaTime) {
// Don't override Death (1) animation.
const bool isSwimmingNow = creatureSwimmingState_.count(guid) > 0;
const bool isWalkingNow = creatureWalkingState_.count(guid) > 0;
const bool isFlyingNow = creatureFlyingState_.count(guid) > 0;
bool prevMoving = creatureWasMoving_[guid];
if (isMovingNow != prevMoving) {
creatureWasMoving_[guid] = isMovingNow;
@ -1492,10 +1494,16 @@ void Application::update(float deltaTime) {
bool gotState = charRenderer->getAnimationState(instanceId, curAnimId, curT, curDur);
if (!gotState || curAnimId != 1 /*Death*/) {
uint32_t targetAnim;
if (isMovingNow)
targetAnim = isSwimmingNow ? 42u : (isWalkingNow ? 4u : 5u); // Swim/Walk/Run
else
targetAnim = isSwimmingNow ? 41u : 0u; // SwimIdle vs Stand
if (isMovingNow) {
if (isFlyingNow) targetAnim = 61u; // Fly (FlyForward)
else if (isSwimmingNow) targetAnim = 42u; // Swim
else if (isWalkingNow) targetAnim = 4u; // Walk
else targetAnim = 5u; // Run
} else {
if (isFlyingNow) targetAnim = 60u; // FlyIdle (hover)
else if (isSwimmingNow) targetAnim = 41u; // SwimIdle
else targetAnim = 0u; // Stand
}
charRenderer->playAnimation(instanceId, targetAnim, /*loop=*/true);
}
}
@ -2810,10 +2818,13 @@ void Application::setupUICallbacks() {
gameHandler->setUnitMoveFlagsCallback([this](uint64_t guid, uint32_t moveFlags) {
const bool isSwimming = (moveFlags & static_cast<uint32_t>(game::MovementFlags::SWIMMING)) != 0;
const bool isWalking = (moveFlags & static_cast<uint32_t>(game::MovementFlags::WALKING)) != 0;
const bool isFlying = (moveFlags & static_cast<uint32_t>(game::MovementFlags::FLYING)) != 0;
if (isSwimming) creatureSwimmingState_[guid] = true;
else creatureSwimmingState_.erase(guid);
if (isWalking) creatureWalkingState_[guid] = true;
else creatureWalkingState_.erase(guid);
if (isFlying) creatureFlyingState_[guid] = true;
else creatureFlyingState_.erase(guid);
});
// Emote animation callback — play server-driven emote animations on NPCs and other players