mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Fix Deeprun Tram: visual movement, direction, and player riding
- Fix NULL renderer pointers by moving TransportManager connection after initializeRenderers for WMO-only maps - Fix tram direction by negating DBC TransportAnimation X/Y local offsets before serverToCanonical conversion - Implement client-side M2 transport boarding via proximity detection (server doesn't send transport attachment for trams) - Use position-delta approach: player keeps normal movement while transport's frame-to-frame motion is applied on top - Prevent server movement packets from clearing client-side M2 transport state (isClientM2Transport guard) - Fix getPlayerWorldPosition for M2 transports: simple canonical addition instead of render-space matrix multiplication
This commit is contained in:
parent
e001aaa2b6
commit
f4c115ade9
5 changed files with 233 additions and 70 deletions
|
|
@ -968,6 +968,15 @@ void Application::update(float deltaTime) {
|
|||
gameHandler->isTaxiMountActive() ||
|
||||
gameHandler->isTaxiActivationPending());
|
||||
bool onTransportNow = gameHandler && gameHandler->isOnTransport();
|
||||
// M2 transports (trams) use position-delta approach: player keeps normal
|
||||
// movement and the transport's frame-to-frame delta is applied on top.
|
||||
// Only WMO transports (ships) use full external-driven mode.
|
||||
bool isM2Transport = false;
|
||||
if (onTransportNow && gameHandler->getTransportManager()) {
|
||||
auto* tr = gameHandler->getTransportManager()->getTransport(gameHandler->getPlayerTransportGuid());
|
||||
isM2Transport = (tr && tr->isM2);
|
||||
}
|
||||
bool onWMOTransport = onTransportNow && !isM2Transport;
|
||||
if (worldEntryMovementGraceTimer_ > 0.0f) {
|
||||
worldEntryMovementGraceTimer_ -= deltaTime;
|
||||
// Clear stale movement from before teleport each frame
|
||||
|
|
@ -976,7 +985,7 @@ void Application::update(float deltaTime) {
|
|||
renderer->getCameraController()->clearMovementInputs();
|
||||
}
|
||||
if (renderer && renderer->getCameraController()) {
|
||||
const bool externallyDrivenMotion = onTaxi || onTransportNow || chargeActive_;
|
||||
const bool externallyDrivenMotion = onTaxi || onWMOTransport || chargeActive_;
|
||||
// Keep physics frozen (externalFollow) during landing clamp when terrain
|
||||
// hasn't loaded yet — prevents gravity from pulling player through void.
|
||||
bool landingClampActive = !onTaxi && taxiLandingClampTimer_ > 0.0f &&
|
||||
|
|
@ -1057,14 +1066,18 @@ void Application::update(float deltaTime) {
|
|||
|
||||
// Sync character render position ↔ canonical WoW coords each frame
|
||||
if (renderer && gameHandler) {
|
||||
bool onTransport = gameHandler->isOnTransport();
|
||||
// For position sync branching, only WMO transports use the dedicated
|
||||
// onTransport branch. M2 transports use the normal movement else branch
|
||||
// with a position-delta correction applied on top.
|
||||
bool onTransport = onWMOTransport;
|
||||
|
||||
// Debug: Log transport state changes
|
||||
static bool wasOnTransport = false;
|
||||
if (onTransport != wasOnTransport) {
|
||||
LOG_DEBUG("Transport state changed: onTransport=", onTransport,
|
||||
bool onTransportNowDbg = gameHandler->isOnTransport();
|
||||
if (onTransportNowDbg != wasOnTransport) {
|
||||
LOG_DEBUG("Transport state changed: onTransport=", onTransportNowDbg,
|
||||
" isM2=", isM2Transport,
|
||||
" guid=0x", std::hex, gameHandler->getPlayerTransportGuid(), std::dec);
|
||||
wasOnTransport = onTransport;
|
||||
wasOnTransport = onTransportNowDbg;
|
||||
}
|
||||
|
||||
if (onTaxi) {
|
||||
|
|
@ -1092,13 +1105,11 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
} else if (onTransport) {
|
||||
// Transport mode: compose world position from transport transform + local offset
|
||||
// WMO transport mode (ships): compose world position from transform + local offset
|
||||
glm::vec3 canonical = gameHandler->getComposedWorldPosition();
|
||||
glm::vec3 renderPos = core::coords::canonicalToRender(canonical);
|
||||
renderer->getCharacterPosition() = renderPos;
|
||||
// Keep movementInfo in lockstep with composed transport world position.
|
||||
gameHandler->setPosition(canonical.x, canonical.y, canonical.z);
|
||||
// Update camera follow target
|
||||
if (renderer->getCameraController()) {
|
||||
glm::vec3* followTarget = renderer->getCameraController()->getFollowTargetMutable();
|
||||
if (followTarget) {
|
||||
|
|
@ -1172,6 +1183,27 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
} else {
|
||||
glm::vec3 renderPos = renderer->getCharacterPosition();
|
||||
|
||||
// M2 transport riding: apply transport's frame-to-frame position delta
|
||||
// so the player moves with the tram while retaining normal movement input.
|
||||
if (isM2Transport && gameHandler->getTransportManager()) {
|
||||
auto* tr = gameHandler->getTransportManager()->getTransport(
|
||||
gameHandler->getPlayerTransportGuid());
|
||||
if (tr) {
|
||||
static glm::vec3 lastTransportCanonical(0);
|
||||
static uint64_t lastTransportGuid = 0;
|
||||
if (lastTransportGuid == gameHandler->getPlayerTransportGuid()) {
|
||||
glm::vec3 deltaCanonical = tr->position - lastTransportCanonical;
|
||||
glm::vec3 deltaRender = core::coords::canonicalToRender(deltaCanonical)
|
||||
- core::coords::canonicalToRender(glm::vec3(0));
|
||||
renderPos += deltaRender;
|
||||
renderer->getCharacterPosition() = renderPos;
|
||||
}
|
||||
lastTransportCanonical = tr->position;
|
||||
lastTransportGuid = gameHandler->getPlayerTransportGuid();
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 canonical = core::coords::renderToCanonical(renderPos);
|
||||
gameHandler->setPosition(canonical.x, canonical.y, canonical.z);
|
||||
|
||||
|
|
@ -1203,6 +1235,41 @@ void Application::update(float deltaTime) {
|
|||
facingSendCooldown_ = 0.1f; // max 10 Hz
|
||||
}
|
||||
}
|
||||
|
||||
// Client-side transport boarding detection (for M2 transports like trams
|
||||
// where the server doesn't send transport attachment data).
|
||||
// Use a generous AABB around each transport's current position.
|
||||
if (gameHandler->getTransportManager() && !gameHandler->isOnTransport()) {
|
||||
auto* tm = gameHandler->getTransportManager();
|
||||
glm::vec3 playerCanonical = core::coords::renderToCanonical(renderPos);
|
||||
|
||||
for (auto& [guid, transport] : tm->getTransports()) {
|
||||
if (!transport.isM2) continue;
|
||||
glm::vec3 diff = playerCanonical - transport.position;
|
||||
float horizDistSq = diff.x * diff.x + diff.y * diff.y;
|
||||
float vertDist = std::abs(diff.z);
|
||||
if (horizDistSq < 144.0f && vertDist < 15.0f) {
|
||||
gameHandler->setPlayerOnTransport(guid, playerCanonical - transport.position);
|
||||
LOG_DEBUG("M2 transport boarding: guid=0x", std::hex, guid, std::dec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// M2 transport disembark: player walked far enough from transport center
|
||||
if (isM2Transport && gameHandler->getTransportManager()) {
|
||||
auto* tm = gameHandler->getTransportManager();
|
||||
auto* tr = tm->getTransport(gameHandler->getPlayerTransportGuid());
|
||||
if (tr) {
|
||||
glm::vec3 playerCanonical = core::coords::renderToCanonical(renderPos);
|
||||
glm::vec3 diff = playerCanonical - tr->position;
|
||||
float horizDistSq = diff.x * diff.x + diff.y * diff.y;
|
||||
if (horizDistSq > 225.0f) {
|
||||
gameHandler->clearPlayerTransport();
|
||||
LOG_DEBUG("M2 transport disembark");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -2073,7 +2140,7 @@ void Application::setupUICallbacks() {
|
|||
}
|
||||
|
||||
uint32_t wmoInstanceId = it->second.instanceId;
|
||||
LOG_DEBUG("Registering server transport: GUID=0x", std::hex, guid, std::dec,
|
||||
LOG_WARNING("Registering server transport: GUID=0x", std::hex, guid, std::dec,
|
||||
" entry=", entry, " displayId=", displayId, " wmoInstance=", wmoInstanceId,
|
||||
" pos=(", x, ", ", y, ", ", z, ")");
|
||||
|
||||
|
|
@ -2101,15 +2168,18 @@ void Application::setupUICallbacks() {
|
|||
hasUsablePath = transportManager->hasUsableMovingPathForEntry(entry, 25.0f);
|
||||
}
|
||||
|
||||
LOG_WARNING("Transport path check: entry=", entry, " hasUsablePath=", hasUsablePath,
|
||||
" preferServerData=", preferServerData, " shipOrZepDisplay=", shipOrZeppelinDisplay);
|
||||
|
||||
if (preferServerData) {
|
||||
// Strict server-authoritative mode: do not infer/remap fallback routes.
|
||||
if (!hasUsablePath) {
|
||||
std::vector<glm::vec3> path = { canonicalSpawnPos };
|
||||
transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
|
||||
LOG_DEBUG("Server-first strict registration: stationary fallback for GUID 0x",
|
||||
LOG_WARNING("Server-first strict registration: stationary fallback for GUID 0x",
|
||||
std::hex, guid, std::dec, " entry=", entry);
|
||||
} else {
|
||||
LOG_DEBUG("Server-first transport registration: using entry DBC path for entry ", entry);
|
||||
LOG_WARNING("Server-first transport registration: using entry DBC path for entry ", entry);
|
||||
}
|
||||
} else if (!hasUsablePath) {
|
||||
// Remap/infer path by spawn position when entry doesn't map 1:1 to DBC ids.
|
||||
|
|
@ -2119,12 +2189,12 @@ void Application::setupUICallbacks() {
|
|||
canonicalSpawnPos, 1200.0f, allowZOnly);
|
||||
if (inferredPath != 0) {
|
||||
pathId = inferredPath;
|
||||
LOG_DEBUG("Using inferred transport path ", pathId, " for entry ", entry);
|
||||
LOG_WARNING("Using inferred transport path ", pathId, " for entry ", entry);
|
||||
} else {
|
||||
uint32_t remappedPath = transportManager->pickFallbackMovingPath(entry, displayId);
|
||||
if (remappedPath != 0) {
|
||||
pathId = remappedPath;
|
||||
LOG_DEBUG("Using remapped fallback transport path ", pathId,
|
||||
LOG_WARNING("Using remapped fallback transport path ", pathId,
|
||||
" for entry ", entry, " displayId=", displayId,
|
||||
" (usableEntryPath=", transportManager->hasPathForEntry(entry), ")");
|
||||
} else {
|
||||
|
|
@ -2137,12 +2207,19 @@ void Application::setupUICallbacks() {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("Using real transport path from TransportAnimation.dbc for entry ", entry);
|
||||
LOG_WARNING("Using real transport path from TransportAnimation.dbc for entry ", entry);
|
||||
}
|
||||
|
||||
// Register the transport with spawn position (prevents rendering at origin until server update)
|
||||
transportManager->registerTransport(guid, wmoInstanceId, pathId, canonicalSpawnPos, entry);
|
||||
|
||||
// Mark M2 transports (e.g. Deeprun Tram cars) so TransportManager uses M2Renderer
|
||||
if (!it->second.isWmo) {
|
||||
if (auto* tr = transportManager->getTransport(guid)) {
|
||||
tr->isM2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Server-authoritative movement - set initial position from spawn data
|
||||
glm::vec3 canonicalPos(x, y, z);
|
||||
transportManager->updateServerTransport(guid, canonicalPos, orientation);
|
||||
|
|
@ -2171,7 +2248,7 @@ void Application::setupUICallbacks() {
|
|||
}
|
||||
|
||||
if (auto* tr = transportManager->getTransport(guid); tr) {
|
||||
LOG_DEBUG("Transport registered: guid=0x", std::hex, guid, std::dec,
|
||||
LOG_WARNING("Transport registered: guid=0x", std::hex, guid, std::dec,
|
||||
" entry=", entry, " displayId=", displayId,
|
||||
" pathId=", tr->pathId,
|
||||
" mode=", (tr->useClientAnimation ? "client" : "server"),
|
||||
|
|
@ -3458,11 +3535,7 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
|
|||
renderer->getTerrainManager()->setMapName(mapName);
|
||||
}
|
||||
|
||||
// Connect TransportManager to WMORenderer (for server transports)
|
||||
if (gameHandler && gameHandler->getTransportManager() && renderer->getWMORenderer()) {
|
||||
gameHandler->getTransportManager()->setWMORenderer(renderer->getWMORenderer());
|
||||
LOG_INFO("TransportManager connected to WMORenderer for online mode");
|
||||
}
|
||||
// NOTE: TransportManager renderer connection moved to after initializeRenderers (later in this function)
|
||||
|
||||
// Connect WMORenderer to M2Renderer (for hierarchical transforms: doodads following WMO parents)
|
||||
if (renderer->getWMORenderer() && renderer->getM2Renderer()) {
|
||||
|
|
@ -3931,9 +4004,18 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
|
|||
renderer->getCameraController()->reset();
|
||||
}
|
||||
|
||||
// Set up test transport (development feature)
|
||||
// Test transport disabled — real transports come from server via UPDATEFLAG_TRANSPORT
|
||||
showProgress("Finalizing world...", 0.94f);
|
||||
setupTestTransport();
|
||||
// setupTestTransport();
|
||||
|
||||
// Connect TransportManager to renderers (must happen AFTER initializeRenderers)
|
||||
if (gameHandler && gameHandler->getTransportManager()) {
|
||||
auto* tm = gameHandler->getTransportManager();
|
||||
if (renderer->getWMORenderer()) tm->setWMORenderer(renderer->getWMORenderer());
|
||||
if (renderer->getM2Renderer()) tm->setM2Renderer(renderer->getM2Renderer());
|
||||
LOG_WARNING("TransportManager connected: wmoR=", (renderer->getWMORenderer() ? "yes" : "NULL"),
|
||||
" m2R=", (renderer->getM2Renderer() ? "yes" : "NULL"));
|
||||
}
|
||||
|
||||
// Set up NPC animation callbacks (for online creatures)
|
||||
showProgress("Preparing creatures...", 0.97f);
|
||||
|
|
@ -6368,6 +6450,10 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t
|
|||
} else if (displayId == 2454 || displayId == 181688 || displayId == 190536) {
|
||||
modelPath = "World\\wmo\\transports\\icebreaker\\Transport_Icebreaker_ship.wmo";
|
||||
LOG_INFO("Overriding transport displayId ", displayId, " → Transport_Icebreaker_ship.wmo");
|
||||
} else if (displayId == 3831) {
|
||||
// Deeprun Tram car
|
||||
modelPath = "World\\Generic\\Gnome\\Passive Doodads\\Subway\\SubwayCar.m2";
|
||||
LOG_WARNING("Overriding transport displayId ", displayId, " → SubwayCar.m2");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6508,7 +6594,12 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t
|
|||
// Transport GameObjects are not always named "transport" in their WMO path
|
||||
// (e.g. elevators/lifts). If the server marks it as a transport, always
|
||||
// notify so TransportManager can animate/carry passengers.
|
||||
if (gameHandler && gameHandler->isTransportGuid(guid)) {
|
||||
bool isTG = gameHandler && gameHandler->isTransportGuid(guid);
|
||||
LOG_WARNING("WMO GO spawned: guid=0x", std::hex, guid, std::dec,
|
||||
" entry=", entry, " displayId=", displayId,
|
||||
" isTransport=", isTG,
|
||||
" pos=(", x, ", ", y, ", ", z, ")");
|
||||
if (isTG) {
|
||||
gameHandler->notifyTransportSpawned(guid, entry, displayId, x, y, z, orientation);
|
||||
}
|
||||
|
||||
|
|
@ -6572,18 +6663,27 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t
|
|||
return;
|
||||
}
|
||||
|
||||
// Freeze animation for static gameobjects, but let portals/effects animate
|
||||
// Freeze animation for static gameobjects, but let portals/effects/transports animate
|
||||
bool isTransportGO = gameHandler && gameHandler->isTransportGuid(guid);
|
||||
std::string lowerPath = modelPath;
|
||||
std::transform(lowerPath.begin(), lowerPath.end(), lowerPath.begin(), ::tolower);
|
||||
bool isAnimatedEffect = (lowerPath.find("instanceportal") != std::string::npos ||
|
||||
lowerPath.find("instancenewportal") != std::string::npos ||
|
||||
lowerPath.find("portalfx") != std::string::npos ||
|
||||
lowerPath.find("spellportal") != std::string::npos);
|
||||
if (!isAnimatedEffect) {
|
||||
if (!isAnimatedEffect && !isTransportGO) {
|
||||
m2Renderer->setInstanceAnimationFrozen(instanceId, true);
|
||||
}
|
||||
|
||||
gameObjectInstances_[guid] = {modelId, instanceId, false};
|
||||
|
||||
// Notify transport system for M2 transports (e.g. Deeprun Tram cars)
|
||||
if (gameHandler && gameHandler->isTransportGuid(guid)) {
|
||||
LOG_WARNING("M2 transport spawned: guid=0x", std::hex, guid, std::dec,
|
||||
" entry=", entry, " displayId=", displayId,
|
||||
" instanceId=", instanceId);
|
||||
gameHandler->notifyTransportSpawned(guid, entry, displayId, x, y, z, orientation);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("Spawned gameobject: guid=0x", std::hex, guid, std::dec,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue