fix(movement): multi-segment path interpolation, waypoint parsing & terrain Z clamping

Add proper waypoint support to entity movement:

- Parse intermediate waypoints from MonsterMove packets in both WotLK
  and Vanilla paths. Uncompressed paths store absolute float3 waypoints;
  compressed paths decode TrinityCore's packed uint32 deltas (11-bit
  signed x/y, 10-bit signed z, ×0.25 scale, waypoint = midpoint − delta)
  with correct 2's-complement sign extension.

- Entity::startMoveAlongPath() interpolates along cumulative-distance-
  proportional segments instead of a single straight line.

- MovementHandler builds the full path (start → waypoints → destination)
  in canonical coords and dispatches to startMoveAlongPath() when
  waypoints are present.

- Snap entity x/y/z to moveEnd in the dead-reckoning overrun phase
  before starting a new movement, preventing visible teleports when the
  renderer was showing the entity at its destination.

- Clamp creature and player entity Z to the terrain surface via
  TerrainManager::getHeightAt() during active movement. Idle entities
  keep their server-authoritative Z to avoid breaking flight masters,
  elevator riders, etc.

Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
This commit is contained in:
Pavel Okhlopkov 2026-04-10 20:35:18 +03:00
parent e07983b7f6
commit 5b47d034c5
5 changed files with 182 additions and 19 deletions

View file

@ -1756,6 +1756,17 @@ void Application::update(float deltaTime) {
inOverrun ? entity->getLatestZ() : entity->getZ());
glm::vec3 renderPos = core::coords::canonicalToRender(canonical);
// Clamp creature Z to terrain surface during movement interpolation.
// The server sends single-segment moves and expects the client to place
// creatures on the ground. Only clamp while actively moving — idle
// creatures keep their server-authoritative Z (flight masters, etc.).
if (entity->isActivelyMoving() && renderer->getTerrainManager()) {
auto terrainZ = renderer->getTerrainManager()->getHeightAt(renderPos.x, renderPos.y);
if (terrainZ.has_value()) {
renderPos.z = terrainZ.value();
}
}
// Visual collision guard: keep hostile melee units from rendering inside the
// player's model while attacking. This is client-side only (no server position change).
// Only check for creatures within 8 units (melee range) — saves expensive
@ -1959,6 +1970,14 @@ void Application::update(float deltaTime) {
inOverrun ? entity->getLatestZ() : entity->getZ());
glm::vec3 renderPos = core::coords::canonicalToRender(canonical);
// Clamp other players' Z to terrain surface during movement
if (entity->isActivelyMoving() && renderer->getTerrainManager()) {
auto terrainZ = renderer->getTerrainManager()->getHeightAt(renderPos.x, renderPos.y);
if (terrainZ.has_value()) {
renderPos.z = terrainZ.value();
}
}
auto posIt = _pCreatureRenderPosCache.find(guid);
if (posIt == _pCreatureRenderPosCache.end()) {
charRenderer->setInstancePosition(instanceId, renderPos);