From 2774b47867ce9a2c382d5c92c12042b4bf833a99 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 13 Feb 2026 20:19:33 -0800 Subject: [PATCH] Fix movement animations and NPC baked textures --- include/game/game_handler.hpp | 1 + src/core/application.cpp | 4 +++- src/game/game_handler.cpp | 33 +++++++++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 04c8332e..98a880f0 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1083,6 +1083,7 @@ private: int visibleItemStride_ = 2; std::unordered_map> otherPlayerVisibleItemEntries_; std::unordered_set otherPlayerVisibleDirty_; + std::unordered_map otherPlayerMoveTimeMs_; // ---- Phase 2: Combat ---- bool autoAttacking = false; diff --git a/src/core/application.cpp b/src/core/application.cpp index 5865e74c..3c8a2280 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -3095,7 +3095,9 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x if (finalTex != 0 && modelData) { for (size_t ti = 0; ti < modelData->textures.size(); ti++) { uint32_t texType = modelData->textures[ti].type; - if (texType == 1 || texType == 2) { + // Humanoid NPCs typically use creature-skin texture types (11-13). + // Some models use 1/2 (character skin/object skin) depending on client/content. + if (texType == 1 || texType == 2 || texType == 11 || texType == 12 || texType == 13) { charRenderer->setModelTexture(modelId, static_cast(ti), finalTex); LOG_DEBUG("Applied baked NPC texture to slot ", ti, " (type ", texType, "): ", bakePath); hasHumanoidTexture = true; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 9b31ca9d..dda8b3ff 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -2846,6 +2846,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { playerDespawnCallback_(guid); otherPlayerVisibleItemEntries_.erase(guid); otherPlayerVisibleDirty_.erase(guid); + otherPlayerMoveTimeMs_.erase(guid); } else if (entity->getType() == ObjectType::GAMEOBJECT && gameObjectDespawnCallback_) { gameObjectDespawnCallback_(guid); } @@ -5210,9 +5211,20 @@ void GameHandler::maybeDetectVisibleItemLayout() { std::array equipEntries{}; int nonZero = 0; + // Prefer authoritative equipped item entry IDs derived from item objects (onlineItems_), + // because Inventory::ItemDef may not be populated yet if templates haven't been queried. for (int i = 0; i < 19; i++) { - const auto& slot = inventory.getEquipSlot(static_cast(i)); - equipEntries[i] = slot.empty() ? 0u : slot.item.itemId; + uint64_t itemGuid = equipSlotGuids_[i]; + if (itemGuid != 0) { + auto it = onlineItems_.find(itemGuid); + if (it != onlineItems_.end() && it->second.entry != 0) { + equipEntries[i] = it->second.entry; + } + } + if (equipEntries[i] == 0) { + const auto& slot = inventory.getEquipSlot(static_cast(i)); + equipEntries[i] = slot.empty() ? 0u : slot.item.itemId; + } if (equipEntries[i] != 0) nonZero++; } if (nonZero < 2) return; @@ -5681,11 +5693,24 @@ void GameHandler::handleOtherPlayerMovement(network::Packet& packet) { // Convert server coords to canonical glm::vec3 canonical = core::coords::serverToCanonical(glm::vec3(info.x, info.y, info.z)); float canYaw = core::coords::serverToCanonicalYaw(info.orientation); - entity->setPosition(canonical.x, canonical.y, canonical.z, canYaw); + // Smooth movement between client-relayed snapshots so animations can play. + uint32_t durationMs = 100; + auto itPrev = otherPlayerMoveTimeMs_.find(moverGuid); + if (itPrev != otherPlayerMoveTimeMs_.end()) { + uint32_t dt = info.time - itPrev->second; // handles wrap + if (dt >= 30 && dt <= 1000) { + if (dt < 50) dt = 50; + if (dt > 350) dt = 350; + durationMs = dt; + } + } + otherPlayerMoveTimeMs_[moverGuid] = info.time; + + entity->startMoveTo(canonical.x, canonical.y, canonical.z, canYaw, durationMs / 1000.0f); // Notify renderer if (creatureMoveCallback_) { - creatureMoveCallback_(moverGuid, canonical.x, canonical.y, canonical.z, 0); + creatureMoveCallback_(moverGuid, canonical.x, canonical.y, canonical.z, durationMs); } }