Infer and animate elevator transport paths

This commit is contained in:
Kelsi 2026-02-12 15:38:39 -08:00
parent c5ecf7d475
commit 76edd3260f
4 changed files with 40 additions and 8 deletions

View file

@ -102,6 +102,12 @@ public:
// Returns 0 when no suitable path match is found.
uint32_t inferMovingPathForSpawn(const glm::vec3& spawnWorldPos, float maxDistance = 1200.0f) const;
// Infer a DBC path by spawn position, optionally including z-only elevator paths.
// Returns 0 when no suitable path match is found.
uint32_t inferDbcPathForSpawn(const glm::vec3& spawnWorldPos,
float maxDistance,
bool allowZOnly) const;
// Choose a deterministic fallback moving DBC path for known server transport entries/displayIds.
// Returns 0 when no suitable moving path is available.
uint32_t pickFallbackMovingPath(uint32_t entry, uint32_t displayId) const;

View file

@ -1250,7 +1250,11 @@ void Application::setupUICallbacks() {
LOG_INFO("Server-first transport registration: using entry DBC path for entry ", entry);
}
} else if (!hasUsablePath) {
uint32_t inferredPath = transportManager->inferMovingPathForSpawn(canonicalSpawnPos);
// Remap/infer path by spawn position when entry doesn't map 1:1 to DBC ids.
// For elevators (TB lift platforms), we must allow z-only paths here.
bool allowZOnly = (displayId == 455 || displayId == 462);
uint32_t inferredPath = transportManager->inferDbcPathForSpawn(
canonicalSpawnPos, 1200.0f, allowZOnly);
if (inferredPath != 0) {
pathId = inferredPath;
LOG_INFO("Using inferred transport path ", pathId, " for entry ", entry);
@ -1359,7 +1363,9 @@ void Application::setupUICallbacks() {
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
}
} else if (!hasUsablePath) {
uint32_t inferredPath = transportManager->inferMovingPathForSpawn(canonicalSpawnPos);
bool allowZOnly = (displayId == 455 || displayId == 462);
uint32_t inferredPath = transportManager->inferDbcPathForSpawn(
canonicalSpawnPos, 1200.0f, allowZOnly);
if (inferredPath != 0) {
pathId = inferredPath;
LOG_INFO("Auto-spawned transport with inferred path: entry=", entry,

View file

@ -2701,6 +2701,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
if (block.updateFlags & 0x0002) {
transportGuids_.insert(block.guid);
LOG_INFO("Detected transport GameObject: 0x", std::hex, block.guid, std::dec,
" entry=", go->getEntry(),
" displayId=", go->getDisplayId(),
" pos=(", go->getX(), ", ", go->getY(), ", ", go->getZ(), ")");
// Note: TransportSpawnCallback will be invoked from Application after WMO instance is created

View file

@ -891,15 +891,23 @@ bool TransportManager::loadTransportAnimationDBC(pipeline::AssetManager* assetMg
float maxX = timedPoints[0].pos.x;
float minY = timedPoints[0].pos.y;
float maxY = timedPoints[0].pos.y;
float minZ = timedPoints[0].pos.z;
float maxZ = timedPoints[0].pos.z;
for (const auto& pt : timedPoints) {
minX = std::min(minX, pt.pos.x);
maxX = std::max(maxX, pt.pos.x);
minY = std::min(minY, pt.pos.y);
maxY = std::max(maxY, pt.pos.y);
minZ = std::min(minZ, pt.pos.z);
maxZ = std::max(maxZ, pt.pos.z);
}
float rangeX = maxX - minX;
float rangeY = maxY - minY;
bool isZOnly = (rangeX < 0.01f && rangeY < 0.01f);
float rangeZ = maxZ - minZ;
float rangeXY = std::max(rangeX, rangeY);
// Some elevator paths have tiny XY jitter. Treat them as z-only when horizontal travel
// is negligible compared to vertical motion.
bool isZOnly = (rangeXY < 0.01f) || (rangeXY < 1.0f && rangeZ > 2.0f);
// Store path
TransportPath path;
@ -921,7 +929,8 @@ bool TransportManager::loadTransportAnimationDBC(pipeline::AssetManager* assetMg
glm::vec3 lastOffset = timedPoints[timedPoints.size() - 2].pos; // -2 to skip wrap duplicate
LOG_INFO(" Transport ", transportEntry, ": ", timedPoints.size() - 1, " waypoints + wrap, ",
durationMs, "ms duration (wrap=", wrapMs, "ms, t0_normalized=", timedPoints[0].tMs, "ms)",
" rangeXY=(", rangeX, ",", rangeY, ") ", (isZOnly ? "[Z-ONLY]" : "[XY-PATH]"),
" rangeXY=(", rangeX, ",", rangeY, ") rangeZ=", rangeZ, " ",
(isZOnly ? "[Z-ONLY]" : "[XY-PATH]"),
" firstOffset=(", firstOffset.x, ", ", firstOffset.y, ", ", firstOffset.z, ")",
" midOffset=(", midOffset.x, ", ", midOffset.y, ", ", midOffset.z, ")",
" lastOffset=(", lastOffset.x, ", ", lastOffset.y, ", ", lastOffset.z, ")");
@ -960,12 +969,17 @@ bool TransportManager::hasUsableMovingPathForEntry(uint32_t entry, float minXYRa
return rangeXY >= minXYRange;
}
uint32_t TransportManager::inferMovingPathForSpawn(const glm::vec3& spawnWorldPos, float maxDistance) const {
uint32_t TransportManager::inferDbcPathForSpawn(const glm::vec3& spawnWorldPos,
float maxDistance,
bool allowZOnly) const {
float bestD2 = maxDistance * maxDistance;
uint32_t bestPathId = 0;
for (const auto& [pathId, path] : paths_) {
if (!path.fromDBC || path.durationMs == 0 || path.zOnly || path.points.empty()) {
if (!path.fromDBC || path.durationMs == 0 || path.points.empty()) {
continue;
}
if (!allowZOnly && path.zOnly) {
continue;
}
@ -981,14 +995,19 @@ uint32_t TransportManager::inferMovingPathForSpawn(const glm::vec3& spawnWorldPo
}
if (bestPathId != 0) {
LOG_INFO("TransportManager: Inferred moving DBC path ", bestPathId,
" for spawn at (", spawnWorldPos.x, ", ", spawnWorldPos.y, ", ", spawnWorldPos.z,
LOG_INFO("TransportManager: Inferred DBC path ", bestPathId,
" (allowZOnly=", allowZOnly ? "yes" : "no",
") for spawn at (", spawnWorldPos.x, ", ", spawnWorldPos.y, ", ", spawnWorldPos.z,
"), dist=", std::sqrt(bestD2));
}
return bestPathId;
}
uint32_t TransportManager::inferMovingPathForSpawn(const glm::vec3& spawnWorldPos, float maxDistance) const {
return inferDbcPathForSpawn(spawnWorldPos, maxDistance, /*allowZOnly=*/false);
}
uint32_t TransportManager::pickFallbackMovingPath(uint32_t entry, uint32_t displayId) const {
auto isUsableMovingPath = [this](uint32_t pathId) -> bool {
auto it = paths_.find(pathId);