mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 08:03:50 +00:00
game/rendering: drive Walk(4) and swim state from movement flags
Add UnitMoveFlagsCallback fired on every MSG_MOVE_* with the raw movement flags field. Application.cpp uses it to update swimming and walking state from any packet, not just explicit START_SWIM/ STOP_SWIM opcodes — fixing cold-join cases where a player is already swimming when we enter the world. Per-frame animation sync now selects Walk(4) when the WALKING flag is set, Run(5) otherwise, and Swim(42)/SwimIdle(41) when swimming. UnitAnimHintCallback is simplified to jump (38=JumpMid) only.
This commit is contained in:
parent
333ada8eb6
commit
d7ebc5c8c7
4 changed files with 41 additions and 24 deletions
|
|
@ -188,7 +188,8 @@ private:
|
||||||
std::unordered_map<uint64_t, uint32_t> creatureModelIds_; // guid → loaded modelId
|
std::unordered_map<uint64_t, uint32_t> creatureModelIds_; // guid → loaded modelId
|
||||||
std::unordered_map<uint64_t, glm::vec3> creatureRenderPosCache_; // guid -> last synced render position
|
std::unordered_map<uint64_t, glm::vec3> creatureRenderPosCache_; // guid -> last synced render position
|
||||||
std::unordered_map<uint64_t, bool> creatureWasMoving_; // guid -> previous-frame movement state
|
std::unordered_map<uint64_t, bool> creatureWasMoving_; // guid -> previous-frame movement state
|
||||||
std::unordered_map<uint64_t, bool> creatureSwimmingState_; // guid -> currently in swim mode
|
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_set<uint64_t> creatureWeaponsAttached_; // guid set when NPC virtual weapons attached
|
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<uint64_t, uint8_t> creatureWeaponAttachAttempts_; // guid -> attach attempts
|
||||||
std::unordered_map<uint32_t, bool> modelIdIsWolfLike_; // modelId → cached wolf/worg check
|
std::unordered_map<uint32_t, bool> modelIdIsWolfLike_; // modelId → cached wolf/worg check
|
||||||
|
|
|
||||||
|
|
@ -637,10 +637,15 @@ public:
|
||||||
using SpellCastAnimCallback = std::function<void(uint64_t guid, bool start, bool isChannel)>;
|
using SpellCastAnimCallback = std::function<void(uint64_t guid, bool start, bool isChannel)>;
|
||||||
void setSpellCastAnimCallback(SpellCastAnimCallback cb) { spellCastAnimCallback_ = std::move(cb); }
|
void setSpellCastAnimCallback(SpellCastAnimCallback cb) { spellCastAnimCallback_ = std::move(cb); }
|
||||||
|
|
||||||
// Unit animation hint: signal jump (animId=38) or swim (animId=42) for other players/NPCs
|
// Unit animation hint: signal jump (animId=38) for other players/NPCs
|
||||||
using UnitAnimHintCallback = std::function<void(uint64_t guid, uint32_t animId)>;
|
using UnitAnimHintCallback = std::function<void(uint64_t guid, uint32_t animId)>;
|
||||||
void setUnitAnimHintCallback(UnitAnimHintCallback cb) { unitAnimHintCallback_ = std::move(cb); }
|
void setUnitAnimHintCallback(UnitAnimHintCallback cb) { unitAnimHintCallback_ = std::move(cb); }
|
||||||
|
|
||||||
|
// Unit move-flags callback: fired on every MSG_MOVE_* for other players with the raw flags field.
|
||||||
|
// Drives Walk(4) vs Run(5) selection and swim state initialization from heartbeat packets.
|
||||||
|
using UnitMoveFlagsCallback = std::function<void(uint64_t guid, uint32_t moveFlags)>;
|
||||||
|
void setUnitMoveFlagsCallback(UnitMoveFlagsCallback cb) { unitMoveFlagsCallback_ = std::move(cb); }
|
||||||
|
|
||||||
// NPC swing callback (plays attack animation on NPC)
|
// NPC swing callback (plays attack animation on NPC)
|
||||||
using NpcSwingCallback = std::function<void(uint64_t guid)>;
|
using NpcSwingCallback = std::function<void(uint64_t guid)>;
|
||||||
void setNpcSwingCallback(NpcSwingCallback cb) { npcSwingCallback_ = std::move(cb); }
|
void setNpcSwingCallback(NpcSwingCallback cb) { npcSwingCallback_ = std::move(cb); }
|
||||||
|
|
@ -2268,6 +2273,7 @@ private:
|
||||||
MeleeSwingCallback meleeSwingCallback_;
|
MeleeSwingCallback meleeSwingCallback_;
|
||||||
SpellCastAnimCallback spellCastAnimCallback_;
|
SpellCastAnimCallback spellCastAnimCallback_;
|
||||||
UnitAnimHintCallback unitAnimHintCallback_;
|
UnitAnimHintCallback unitAnimHintCallback_;
|
||||||
|
UnitMoveFlagsCallback unitMoveFlagsCallback_;
|
||||||
NpcSwingCallback npcSwingCallback_;
|
NpcSwingCallback npcSwingCallback_;
|
||||||
NpcGreetingCallback npcGreetingCallback_;
|
NpcGreetingCallback npcGreetingCallback_;
|
||||||
NpcFarewellCallback npcFarewellCallback_;
|
NpcFarewellCallback npcFarewellCallback_;
|
||||||
|
|
|
||||||
|
|
@ -751,6 +751,7 @@ void Application::logoutToLogin() {
|
||||||
creatureWeaponAttachAttempts_.clear();
|
creatureWeaponAttachAttempts_.clear();
|
||||||
creatureWasMoving_.clear();
|
creatureWasMoving_.clear();
|
||||||
creatureSwimmingState_.clear();
|
creatureSwimmingState_.clear();
|
||||||
|
creatureWalkingState_.clear();
|
||||||
deadCreatureGuids_.clear();
|
deadCreatureGuids_.clear();
|
||||||
nonRenderableCreatureDisplayIds_.clear();
|
nonRenderableCreatureDisplayIds_.clear();
|
||||||
creaturePermanentFailureGuids_.clear();
|
creaturePermanentFailureGuids_.clear();
|
||||||
|
|
@ -1477,11 +1478,13 @@ void Application::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
posIt->second = renderPos;
|
posIt->second = renderPos;
|
||||||
|
|
||||||
// Drive movement animation: Run/Swim (5/42) when moving, Stand/SwimIdle (0/41) when idle.
|
// Drive movement animation: Walk/Run/Swim (4/5/42) when moving,
|
||||||
|
// Stand/SwimIdle (0/41) when idle. Walk(4) selected when WALKING flag is set.
|
||||||
// WoW M2 animation IDs: 4=Walk, 5=Run, 41=SwimIdle, 42=Swim.
|
// WoW M2 animation IDs: 4=Walk, 5=Run, 41=SwimIdle, 42=Swim.
|
||||||
// Only switch on transitions to avoid resetting animation time.
|
// Only switch on transitions to avoid resetting animation time.
|
||||||
// Don't override Death (1) animation.
|
// Don't override Death (1) animation.
|
||||||
const bool isSwimmingNow = creatureSwimmingState_.count(guid) > 0;
|
const bool isSwimmingNow = creatureSwimmingState_.count(guid) > 0;
|
||||||
|
const bool isWalkingNow = creatureWalkingState_.count(guid) > 0;
|
||||||
bool prevMoving = creatureWasMoving_[guid];
|
bool prevMoving = creatureWasMoving_[guid];
|
||||||
if (isMovingNow != prevMoving) {
|
if (isMovingNow != prevMoving) {
|
||||||
creatureWasMoving_[guid] = isMovingNow;
|
creatureWasMoving_[guid] = isMovingNow;
|
||||||
|
|
@ -1490,7 +1493,7 @@ void Application::update(float deltaTime) {
|
||||||
if (!gotState || curAnimId != 1 /*Death*/) {
|
if (!gotState || curAnimId != 1 /*Death*/) {
|
||||||
uint32_t targetAnim;
|
uint32_t targetAnim;
|
||||||
if (isMovingNow)
|
if (isMovingNow)
|
||||||
targetAnim = isSwimmingNow ? 42u : 5u; // Swim vs Run
|
targetAnim = isSwimmingNow ? 42u : (isWalkingNow ? 4u : 5u); // Swim/Walk/Run
|
||||||
else
|
else
|
||||||
targetAnim = isSwimmingNow ? 41u : 0u; // SwimIdle vs Stand
|
targetAnim = isSwimmingNow ? 41u : 0u; // SwimIdle vs Stand
|
||||||
charRenderer->playAnimation(instanceId, targetAnim, /*loop=*/true);
|
charRenderer->playAnimation(instanceId, targetAnim, /*loop=*/true);
|
||||||
|
|
@ -2777,20 +2780,10 @@ void Application::setupUICallbacks() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Unit animation hint callback — play jump (38) or swim (42) on other players/NPCs.
|
// Unit animation hint callback — plays jump (38=JumpMid) animation on other players/NPCs.
|
||||||
// animId=42 (Swim): marks entity as swimming; per-frame sync will use SwimIdle(41) when stopped.
|
// Swim/walking state is now authoritative from the move-flags callback below.
|
||||||
// animId=0: clears swim state (MSG_MOVE_STOP_SWIM); per-frame sync reverts to Stand(0).
|
|
||||||
// animId=38 (JumpMid): airborne jump animation; land detection is via per-frame sync.
|
// animId=38 (JumpMid): airborne jump animation; land detection is via per-frame sync.
|
||||||
gameHandler->setUnitAnimHintCallback([this](uint64_t guid, uint32_t animId) {
|
gameHandler->setUnitAnimHintCallback([this](uint64_t guid, uint32_t animId) {
|
||||||
// Track swim state regardless of whether the instance is visible yet.
|
|
||||||
if (animId == 42u) {
|
|
||||||
creatureSwimmingState_[guid] = true;
|
|
||||||
} else if (animId == 0u) {
|
|
||||||
creatureSwimmingState_.erase(guid);
|
|
||||||
// Don't play Stand here — per-frame sync will do it when movement ceases.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!renderer) return;
|
if (!renderer) return;
|
||||||
auto* cr = renderer->getCharacterRenderer();
|
auto* cr = renderer->getCharacterRenderer();
|
||||||
if (!cr) return;
|
if (!cr) return;
|
||||||
|
|
@ -2810,6 +2803,19 @@ void Application::setupUICallbacks() {
|
||||||
cr->playAnimation(instanceId, animId, /*loop=*/true);
|
cr->playAnimation(instanceId, animId, /*loop=*/true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Unit move-flags callback — updates swimming and walking state from every MSG_MOVE_* packet.
|
||||||
|
// This is more reliable than opcode-based hints for cold joins and heartbeats:
|
||||||
|
// a player already swimming when we join will have SWIMMING set on the first heartbeat.
|
||||||
|
// Walking(4) vs Running(5) is also driven here from the WALKING flag.
|
||||||
|
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;
|
||||||
|
if (isSwimming) creatureSwimmingState_[guid] = true;
|
||||||
|
else creatureSwimmingState_.erase(guid);
|
||||||
|
if (isWalking) creatureWalkingState_[guid] = true;
|
||||||
|
else creatureWalkingState_.erase(guid);
|
||||||
|
});
|
||||||
|
|
||||||
// Emote animation callback — play server-driven emote animations on NPCs and other players
|
// Emote animation callback — play server-driven emote animations on NPCs and other players
|
||||||
gameHandler->setEmoteAnimCallback([this](uint64_t guid, uint32_t emoteAnim) {
|
gameHandler->setEmoteAnimCallback([this](uint64_t guid, uint32_t emoteAnim) {
|
||||||
if (!renderer || emoteAnim == 0) return;
|
if (!renderer || emoteAnim == 0) return;
|
||||||
|
|
@ -6927,6 +6933,7 @@ void Application::despawnOnlinePlayer(uint64_t guid) {
|
||||||
onlinePlayerAppearance_.erase(guid);
|
onlinePlayerAppearance_.erase(guid);
|
||||||
pendingOnlinePlayerEquipment_.erase(guid);
|
pendingOnlinePlayerEquipment_.erase(guid);
|
||||||
creatureSwimmingState_.erase(guid);
|
creatureSwimmingState_.erase(guid);
|
||||||
|
creatureWalkingState_.erase(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation) {
|
void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation) {
|
||||||
|
|
@ -8521,6 +8528,7 @@ void Application::despawnOnlineCreature(uint64_t guid) {
|
||||||
creatureWeaponAttachAttempts_.erase(guid);
|
creatureWeaponAttachAttempts_.erase(guid);
|
||||||
creatureWasMoving_.erase(guid);
|
creatureWasMoving_.erase(guid);
|
||||||
creatureSwimmingState_.erase(guid);
|
creatureSwimmingState_.erase(guid);
|
||||||
|
creatureWalkingState_.erase(guid);
|
||||||
|
|
||||||
LOG_DEBUG("Despawned creature: guid=0x", std::hex, guid, std::dec);
|
LOG_DEBUG("Despawned creature: guid=0x", std::hex, guid, std::dec);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12174,7 +12174,6 @@ void GameHandler::handleOtherPlayerMovement(network::Packet& packet) {
|
||||||
(wireOp == wireOpcode(Opcode::MSG_MOVE_STOP_SWIM)) ||
|
(wireOp == wireOpcode(Opcode::MSG_MOVE_STOP_SWIM)) ||
|
||||||
(wireOp == wireOpcode(Opcode::MSG_MOVE_FALL_LAND));
|
(wireOp == wireOpcode(Opcode::MSG_MOVE_FALL_LAND));
|
||||||
const bool isJumpOpcode = (wireOp == wireOpcode(Opcode::MSG_MOVE_JUMP));
|
const bool isJumpOpcode = (wireOp == wireOpcode(Opcode::MSG_MOVE_JUMP));
|
||||||
const bool isSwimOpcode = (wireOp == wireOpcode(Opcode::MSG_MOVE_START_SWIM));
|
|
||||||
|
|
||||||
// For stop opcodes snap the entity position (duration=0) so it doesn't keep interpolating,
|
// For stop opcodes snap the entity position (duration=0) so it doesn't keep interpolating,
|
||||||
// and pass durationMs=0 to the renderer so the Run-anim flash is suppressed.
|
// and pass durationMs=0 to the renderer so the Run-anim flash is suppressed.
|
||||||
|
|
@ -12189,13 +12188,16 @@ void GameHandler::handleOtherPlayerMovement(network::Packet& packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal specific animation transitions that the per-frame sync can't detect reliably.
|
// Signal specific animation transitions that the per-frame sync can't detect reliably.
|
||||||
// WoW M2 animation IDs: 38=JumpMid (loops during airborne), 42=Swim
|
// WoW M2 animation ID 38=JumpMid (loops during airborne).
|
||||||
// animId=0 signals "exit swim mode" (MSG_MOVE_STOP_SWIM) so per-frame sync reverts to Stand.
|
// Swim/walking state is now authoritative from the movement flags field via unitMoveFlagsCallback_.
|
||||||
const bool isStopSwimOpcode = (wireOp == wireOpcode(Opcode::MSG_MOVE_STOP_SWIM));
|
if (unitAnimHintCallback_ && isJumpOpcode) {
|
||||||
if (unitAnimHintCallback_) {
|
unitAnimHintCallback_(moverGuid, 38u);
|
||||||
if (isJumpOpcode) unitAnimHintCallback_(moverGuid, 38u);
|
}
|
||||||
else if (isSwimOpcode) unitAnimHintCallback_(moverGuid, 42u);
|
|
||||||
else if (isStopSwimOpcode) unitAnimHintCallback_(moverGuid, 0u);
|
// Fire move-flags callback so application.cpp can update swimming/walking state
|
||||||
|
// from the flags field embedded in every movement packet (covers heartbeats and cold joins).
|
||||||
|
if (unitMoveFlagsCallback_) {
|
||||||
|
unitMoveFlagsCallback_(moverGuid, info.flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue