Fix movement animations and NPC baked textures

This commit is contained in:
Kelsi 2026-02-13 20:19:33 -08:00
parent 65fe5e8e1d
commit 2774b47867
3 changed files with 33 additions and 5 deletions

View file

@ -1083,6 +1083,7 @@ private:
int visibleItemStride_ = 2;
std::unordered_map<uint64_t, std::array<uint32_t, 19>> otherPlayerVisibleItemEntries_;
std::unordered_set<uint64_t> otherPlayerVisibleDirty_;
std::unordered_map<uint64_t, uint32_t> otherPlayerMoveTimeMs_;
// ---- Phase 2: Combat ----
bool autoAttacking = false;

View file

@ -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<uint32_t>(ti), finalTex);
LOG_DEBUG("Applied baked NPC texture to slot ", ti, " (type ", texType, "): ", bakePath);
hasHumanoidTexture = true;

View file

@ -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<uint32_t, 19> 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<EquipSlot>(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<EquipSlot>(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);
}
}