diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index d3ea187a..f9bf9cab 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -635,6 +635,11 @@ void GameHandler::update(float deltaTime) { entity->updateMovement(deltaTime); continue; } + // Keep selected/engaged target interpolation exact for UI targeting circle. + if (guid == targetGuid || guid == autoAttackTarget) { + entity->updateMovement(deltaTime); + continue; + } // Distance cull other entities (use latest position to avoid culling by stale origin) glm::vec3 entityPos(entity->getLatestX(), entity->getLatestY(), entity->getLatestZ()); diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index 6ef11f86..830fa9a1 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -1075,7 +1075,7 @@ void CharacterRenderer::update(float deltaTime, const glm::vec3& cameraPos) { inst.position = inst.moveEnd; inst.isMoving = false; // Return to idle when movement completes - if (inst.currentAnimationId == 4) { + if (inst.currentAnimationId == 4 || inst.currentAnimationId == 5) { playAnimation(id, 0, true); } } else { @@ -1706,15 +1706,28 @@ void CharacterRenderer::moveInstanceTo(uint32_t instanceId, const glm::vec3& des // Don't move dead instances (corpses shouldn't slide around) if (inst.isDead) return; + auto pickMoveAnim = [&]() -> uint32_t { + // Prefer run when available to avoid "gliding with attack pose" on chase. + if (hasAnimation(instanceId, 5)) return 5; // Run + if (hasAnimation(instanceId, 4)) return 4; // Walk + return 0; + }; + + float planarDist = glm::length(glm::vec2(destination.x - inst.position.x, + destination.y - inst.position.y)); if (durationSeconds <= 0.0f) { - // Instant move (stop) - inst.position = destination; - inst.isMoving = false; - // Return to idle animation if currently walking - if (inst.currentAnimationId == 4) { - playAnimation(instanceId, 0, true); + if (planarDist < 0.01f) { + // Stop at current location. + inst.position = destination; + inst.isMoving = false; + if (inst.currentAnimationId == 4 || inst.currentAnimationId == 5) { + playAnimation(instanceId, 0, true); + } + return; } - return; + // Some cores send movement-only deltas without spline duration. + // Synthesize a tiny duration so movement anim/rotation still updates. + durationSeconds = std::clamp(planarDist / 7.0f, 0.05f, 0.20f); } inst.moveStart = inst.position; @@ -1730,9 +1743,10 @@ void CharacterRenderer::moveInstanceTo(uint32_t instanceId, const glm::vec3& des inst.rotation.z = angle; } - // Play walk animation (ID 4) while moving - if (inst.currentAnimationId == 0) { - playAnimation(instanceId, 4, true); + // Play movement animation while moving. + uint32_t moveAnim = pickMoveAnim(); + if (moveAnim != 0 && inst.currentAnimationId != moveAnim) { + playAnimation(instanceId, moveAnim, true); } } diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 6a373a9c..d6f011e5 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -392,7 +392,8 @@ void GameScreen::render(game::GameHandler& gameHandler) { if (gameHandler.hasTarget()) { auto target = gameHandler.getTarget(); if (target) { - targetGLPos = core::coords::canonicalToRender(glm::vec3(target->getX(), target->getY(), target->getZ())); + targetGLPos = core::coords::canonicalToRender( + glm::vec3(target->getLatestX(), target->getLatestY(), target->getLatestZ())); renderer->setTargetPosition(&targetGLPos); // Selection circle color: WoW-canonical level-based colors