Fix transport update handling, add desktop/icon resources, and clean repo artifacts

This commit is contained in:
Kelsi 2026-02-11 15:24:05 -08:00
parent 0a51ec8dda
commit c20d5441d0
29 changed files with 284 additions and 41 deletions

1
.gitignore vendored
View file

@ -67,3 +67,4 @@ cache/
# Single-player saves # Single-player saves
saves/ saves/
wowee_[0-9][0-9][0-9][0-9]

View file

@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(wowee VERSION 1.0.0 LANGUAGES CXX) project(wowee VERSION 1.0.0 LANGUAGES CXX)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -264,8 +265,13 @@ set(WOWEE_HEADERS
include/ui/talent_screen.hpp include/ui/talent_screen.hpp
) )
set(WOWEE_PLATFORM_SOURCES)
if(WIN32)
list(APPEND WOWEE_PLATFORM_SOURCES resources/wowee.rc)
endif()
# Create executable # Create executable
add_executable(wowee ${WOWEE_SOURCES} ${WOWEE_HEADERS}) add_executable(wowee ${WOWEE_SOURCES} ${WOWEE_HEADERS} ${WOWEE_PLATFORM_SOURCES})
# Include directories # Include directories
target_include_directories(wowee PRIVATE target_include_directories(wowee PRIVATE
@ -341,6 +347,22 @@ install(TARGETS wowee
ARCHIVE DESTINATION lib ARCHIVE DESTINATION lib
) )
# Linux desktop integration (launcher + icon)
if(UNIX AND NOT APPLE)
set(WOWEE_LINUX_ICON_PATH "${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons/hicolor/256x256/apps/wowee.png")
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/resources/wowee.desktop.in
${CMAKE_CURRENT_BINARY_DIR}/wowee.desktop
@ONLY
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/wowee.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/assets/Wowee.png
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps
RENAME wowee.png)
endif()
# Print configuration summary # Print configuration summary
message(STATUS "") message(STATUS "")
message(STATUS "Wowee Configuration:") message(STATUS "Wowee Configuration:")

View file

@ -28,6 +28,7 @@ struct TransportPath {
bool looping; // Set to false after adding explicit wrap point bool looping; // Set to false after adding explicit wrap point
uint32_t durationMs; // Total loop duration in ms (includes wrap segment if added) uint32_t durationMs; // Total loop duration in ms (includes wrap segment if added)
bool zOnly; // True if path only has Z movement (elevator/bobbing), false if real XY travel bool zOnly; // True if path only has Z movement (elevator/bobbing), false if real XY travel
bool fromDBC; // True if loaded from TransportAnimation.dbc, false for runtime fallback/custom paths
}; };
struct ActiveTransport { struct ActiveTransport {
@ -85,6 +86,15 @@ public:
// Check if a path exists for a given GameObject entry // Check if a path exists for a given GameObject entry
bool hasPathForEntry(uint32_t entry) const; bool hasPathForEntry(uint32_t entry) const;
// Infer a real moving DBC path by spawn position (for servers whose transport entry IDs
// don't map 1:1 to TransportAnimation.dbc entry IDs).
// Returns 0 when no suitable path match is found.
uint32_t inferMovingPathForSpawn(const glm::vec3& spawnWorldPos, float maxDistance = 1200.0f) 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;
// Update server-controlled transport position/rotation directly (bypasses path movement) // Update server-controlled transport position/rotation directly (bypasses path movement)
void updateServerTransport(uint64_t guid, const glm::vec3& position, float orientation); void updateServerTransport(uint64_t guid, const glm::vec3& position, float orientation);

View file

@ -418,6 +418,8 @@ struct MovementInfo {
float transportZ = 0.0f; float transportZ = 0.0f;
float transportO = 0.0f; // Local orientation on transport float transportO = 0.0f; // Local orientation on transport
uint32_t transportTime = 0; // Transport movement timestamp uint32_t transportTime = 0; // Transport movement timestamp
int8_t transportSeat = -1; // Transport seat (-1 when unknown/not seated)
uint32_t transportTime2 = 0; // Secondary transport time (when interpolated movement flag is set)
bool hasFlag(MovementFlags flag) const { bool hasFlag(MovementFlags flag) const {
return (flags & static_cast<uint32_t>(flag)) != 0; return (flags & static_cast<uint32_t>(flag)) != 0;

View file

@ -0,0 +1,8 @@
[Desktop Entry]
Type=Application
Name=Wowee
Comment=Custom World of Warcraft WotLK compatible game client
Exec=wowee
Icon=@WOWEE_LINUX_ICON_PATH@
Terminal=false
Categories=Game;

1
resources/wowee.rc Normal file
View file

@ -0,0 +1 @@
IDI_APP_ICON ICON "assets\\wowee.ico"

View file

@ -888,14 +888,28 @@ void Application::setupUICallbacks() {
// Coordinates are already canonical (converted in game_handler.cpp when entity was created) // Coordinates are already canonical (converted in game_handler.cpp when entity was created)
glm::vec3 canonicalSpawnPos(x, y, z); glm::vec3 canonicalSpawnPos(x, y, z);
// Check if we have a real path from TransportAnimation.dbc (indexed by entry) // Check if we have a real path from TransportAnimation.dbc (indexed by entry).
// AzerothCore transport entries are not always 1:1 with DBC path ids.
if (!transportManager->hasPathForEntry(entry)) { if (!transportManager->hasPathForEntry(entry)) {
LOG_WARNING("No TransportAnimation.dbc path for entry ", entry, uint32_t remappedPath = transportManager->pickFallbackMovingPath(entry, displayId);
" - transport will be stationary"); if (remappedPath != 0) {
pathId = remappedPath;
LOG_INFO("Using remapped fallback transport path ", pathId,
" for entry ", entry, " displayId=", displayId);
} else {
uint32_t inferredPath = transportManager->inferMovingPathForSpawn(canonicalSpawnPos);
if (inferredPath != 0) {
pathId = inferredPath;
LOG_INFO("Using inferred transport path ", pathId, " for entry ", entry);
} else {
LOG_WARNING("No TransportAnimation.dbc path for entry ", entry,
" - transport will be stationary");
// Fallback: Stationary at spawn point (wait for server to send real position) // Fallback: Stationary at spawn point (wait for server to send real position)
std::vector<glm::vec3> path = { canonicalSpawnPos }; std::vector<glm::vec3> path = { canonicalSpawnPos };
transportManager->loadPathFromNodes(pathId, path, false, 0.0f); transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
}
}
} else { } else {
LOG_INFO("Using real transport path from TransportAnimation.dbc for entry ", entry); LOG_INFO("Using real transport path from TransportAnimation.dbc for entry ", entry);
} }
@ -943,12 +957,28 @@ void Application::setupUICallbacks() {
// Coordinates are already canonical (converted in game_handler.cpp) // Coordinates are already canonical (converted in game_handler.cpp)
glm::vec3 canonicalSpawnPos(x, y, z); glm::vec3 canonicalSpawnPos(x, y, z);
// Check if we have a real path, otherwise create stationary fallback // Check if we have a real path, otherwise remap/infer/fall back to stationary.
if (!transportManager->hasPathForEntry(entry)) { if (!transportManager->hasPathForEntry(entry)) {
std::vector<glm::vec3> path = { canonicalSpawnPos }; uint32_t remappedPath = transportManager->pickFallbackMovingPath(entry, displayId);
transportManager->loadPathFromNodes(pathId, path, false, 0.0f); if (remappedPath != 0) {
LOG_INFO("Auto-spawned transport with stationary path: entry=", entry, pathId = remappedPath;
" displayId=", displayId, " wmoInstance=", wmoInstanceId); LOG_INFO("Auto-spawned transport with remapped fallback path: entry=", entry,
" remappedPath=", pathId, " displayId=", displayId,
" wmoInstance=", wmoInstanceId);
} else {
uint32_t inferredPath = transportManager->inferMovingPathForSpawn(canonicalSpawnPos);
if (inferredPath != 0) {
pathId = inferredPath;
LOG_INFO("Auto-spawned transport with inferred path: entry=", entry,
" inferredPath=", pathId, " displayId=", displayId,
" wmoInstance=", wmoInstanceId);
} else {
std::vector<glm::vec3> path = { canonicalSpawnPos };
transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
LOG_INFO("Auto-spawned transport with stationary path: entry=", entry,
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
}
}
} else { } else {
LOG_INFO("Auto-spawned transport with real path: entry=", entry, LOG_INFO("Auto-spawned transport with real path: entry=", entry,
" displayId=", displayId, " wmoInstance=", wmoInstanceId); " displayId=", displayId, " wmoInstance=", wmoInstanceId);

View file

@ -1782,12 +1782,29 @@ void GameHandler::sendMovement(Opcode opcode) {
movementInfo.transportX = playerTransportOffset_.x; movementInfo.transportX = playerTransportOffset_.x;
movementInfo.transportY = playerTransportOffset_.y; movementInfo.transportY = playerTransportOffset_.y;
movementInfo.transportZ = playerTransportOffset_.z; movementInfo.transportZ = playerTransportOffset_.z;
movementInfo.transportO = movementInfo.orientation; // Use same orientation as player movementInfo.transportTime = movementInfo.time;
movementInfo.transportTime = movementInfo.time; // Use same timestamp movementInfo.transportSeat = -1;
movementInfo.transportTime2 = movementInfo.time;
// ONTRANSPORT expects local orientation (player yaw relative to transport yaw).
float transportYaw = 0.0f;
if (transportManager_) {
if (auto* tr = transportManager_->getTransport(playerTransportGuid_); tr && tr->hasServerYaw) {
transportYaw = tr->serverYaw;
}
}
float localTransportO = movementInfo.orientation - transportYaw;
constexpr float kPi = 3.14159265359f;
constexpr float kTwoPi = 6.28318530718f;
while (localTransportO > kPi) localTransportO -= kTwoPi;
while (localTransportO < -kPi) localTransportO += kTwoPi;
movementInfo.transportO = localTransportO;
} else { } else {
// Clear transport flag if not on transport // Clear transport flag if not on transport
movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::ONTRANSPORT); movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::ONTRANSPORT);
movementInfo.transportGuid = 0; movementInfo.transportGuid = 0;
movementInfo.transportSeat = -1;
} }
LOG_DEBUG("Sending movement packet: opcode=0x", std::hex, LOG_DEBUG("Sending movement packet: opcode=0x", std::hex,
@ -1907,6 +1924,26 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
playerTransportOffset_ = glm::vec3(0.0f); playerTransportOffset_ = glm::vec3(0.0f);
} }
} }
// GameObjects with UPDATEFLAG_POSITION carry a parent transport GUID and local offset.
// Use that to drive parent transport motion and compose correct child world position.
if (block.objectType == ObjectType::GAMEOBJECT &&
(block.updateFlags & 0x0100) &&
block.onTransport &&
block.transportGuid != 0) {
glm::vec3 localOffset = core::coords::serverToCanonical(
glm::vec3(block.transportX, block.transportY, block.transportZ));
// Refresh parent transport transform from this packet stream.
if (transportMoveCallback_) {
transportMoveCallback_(block.transportGuid, pos.x, pos.y, pos.z, block.orientation);
}
if (transportManager_ && transportManager_->getTransport(block.transportGuid)) {
glm::vec3 composed = transportManager_->getPlayerWorldPosition(block.transportGuid, localOffset);
entity->setPosition(composed.x, composed.y, composed.z, entity->getOrientation());
}
}
} }
// Set fields // Set fields
@ -2381,6 +2418,26 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
glm::vec3 pos = core::coords::serverToCanonical(glm::vec3(block.x, block.y, block.z)); glm::vec3 pos = core::coords::serverToCanonical(glm::vec3(block.x, block.y, block.z));
entity->setPosition(pos.x, pos.y, pos.z, block.orientation); entity->setPosition(pos.x, pos.y, pos.z, block.orientation);
LOG_DEBUG("Updated entity position: 0x", std::hex, block.guid, std::dec); LOG_DEBUG("Updated entity position: 0x", std::hex, block.guid, std::dec);
// Some GameObject movement blocks are transport-relative: the packet carries
// parent transport GUID + local child offset in UPDATEFLAG_POSITION.
if (entity->getType() == ObjectType::GAMEOBJECT &&
(block.updateFlags & 0x0100) &&
block.onTransport &&
block.transportGuid != 0) {
glm::vec3 localOffset = core::coords::serverToCanonical(
glm::vec3(block.transportX, block.transportY, block.transportZ));
if (transportMoveCallback_) {
transportMoveCallback_(block.transportGuid, pos.x, pos.y, pos.z, block.orientation);
}
if (transportManager_ && transportManager_->getTransport(block.transportGuid)) {
glm::vec3 composed = transportManager_->getPlayerWorldPosition(block.transportGuid, localOffset);
entity->setPosition(composed.x, composed.y, composed.z, entity->getOrientation());
}
}
if (block.guid == playerGuid) { if (block.guid == playerGuid) {
movementInfo.x = pos.x; movementInfo.x = pos.x;
movementInfo.y = pos.y; movementInfo.y = pos.y;

View file

@ -140,6 +140,7 @@ void TransportManager::loadPathFromNodes(uint32_t pathId, const std::vector<glm:
TransportPath path; TransportPath path;
path.pathId = pathId; path.pathId = pathId;
path.zOnly = false; // Manually loaded paths are assumed to have XY movement path.zOnly = false; // Manually loaded paths are assumed to have XY movement
path.fromDBC = false;
// Helper: compute segment duration from distance and speed // Helper: compute segment duration from distance and speed
auto segMsFromDist = [&](float dist) -> uint32_t { auto segMsFromDist = [&](float dist) -> uint32_t {
@ -237,12 +238,21 @@ void TransportManager::updateTransportMovement(ActiveTransport& transport, float
transport.localClockMs += (uint32_t)(deltaTime * 1000.0f); transport.localClockMs += (uint32_t)(deltaTime * 1000.0f);
pathTimeMs = transport.localClockMs % path.durationMs; pathTimeMs = transport.localClockMs % path.durationMs;
} else { } else {
// Server-driven but no clock yet - don't move // Server-driven but no clock yet. If updates never arrive, fall back to local animation.
updateTransformMatrices(transport); constexpr float kMissingUpdateFallbackSec = 2.5f;
if (wmoRenderer_) { if ((elapsedTime_ - transport.lastServerUpdate) >= kMissingUpdateFallbackSec) {
wmoRenderer_->setInstanceTransform(transport.wmoInstanceId, transport.transform); transport.useClientAnimation = true;
transport.localClockMs = 0;
pathTimeMs = 0;
LOG_WARNING("TransportManager: No server movement updates for transport 0x", std::hex, transport.guid, std::dec,
" after ", kMissingUpdateFallbackSec, "s - enabling client fallback animation");
} else {
updateTransformMatrices(transport);
if (wmoRenderer_) {
wmoRenderer_->setInstanceTransform(transport.wmoInstanceId, transport.transform);
}
return;
} }
return;
} }
// Evaluate position from time (path is local offsets, add base position) // Evaluate position from time (path is local offsets, add base position)
@ -496,6 +506,7 @@ void TransportManager::updateServerTransport(uint64_t guid, const glm::vec3& pos
// Track server updates // Track server updates
transport->serverUpdateCount++; transport->serverUpdateCount++;
transport->lastServerUpdate = elapsedTime_; transport->lastServerUpdate = elapsedTime_;
transport->useClientAnimation = false; // Server updates take precedence
auto pathIt = paths_.find(transport->pathId); auto pathIt = paths_.find(transport->pathId);
if (pathIt == paths_.end() || pathIt->second.durationMs == 0) { if (pathIt == paths_.end() || pathIt->second.durationMs == 0) {
@ -836,6 +847,7 @@ bool TransportManager::loadTransportAnimationDBC(pipeline::AssetManager* assetMg
path.looping = false; path.looping = false;
path.durationMs = durationMs; path.durationMs = durationMs;
path.zOnly = isZOnly; path.zOnly = isZOnly;
path.fromDBC = true;
paths_[transportEntry] = path; paths_[transportEntry] = path;
pathsLoaded++; pathsLoaded++;
@ -857,7 +869,95 @@ bool TransportManager::loadTransportAnimationDBC(pipeline::AssetManager* assetMg
} }
bool TransportManager::hasPathForEntry(uint32_t entry) const { bool TransportManager::hasPathForEntry(uint32_t entry) const {
return paths_.find(entry) != paths_.end(); auto it = paths_.find(entry);
return it != paths_.end() && it->second.fromDBC;
}
uint32_t TransportManager::inferMovingPathForSpawn(const glm::vec3& spawnWorldPos, float maxDistance) 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()) {
continue;
}
// Find nearest waypoint on this path to spawn.
for (const auto& p : path.points) {
glm::vec3 diff = p.pos - spawnWorldPos;
float d2 = glm::dot(diff, diff);
if (d2 < bestD2) {
bestD2 = d2;
bestPathId = pathId;
}
}
}
if (bestPathId != 0) {
LOG_INFO("TransportManager: Inferred moving DBC path ", bestPathId,
" for spawn at (", spawnWorldPos.x, ", ", spawnWorldPos.y, ", ", spawnWorldPos.z,
"), dist=", std::sqrt(bestD2));
}
return bestPathId;
}
uint32_t TransportManager::pickFallbackMovingPath(uint32_t entry, uint32_t displayId) const {
auto isUsableMovingPath = [this](uint32_t pathId) -> bool {
auto it = paths_.find(pathId);
if (it == paths_.end()) return false;
const auto& path = it->second;
return path.fromDBC && !path.zOnly && path.durationMs > 0 && path.points.size() > 1;
};
// Known AzerothCore transport entry remaps (WotLK): server entry -> moving DBC path id.
// These entries commonly do not match TransportAnimation.dbc ids 1:1.
static const std::unordered_map<uint32_t, uint32_t> kEntryRemap = {
{176231u, 176080u}, // The Maiden's Fancy
{176310u, 176081u}, // The Bravery
{20808u, 176082u}, // The Black Princess
{164871u, 193182u}, // The Thundercaller
{176495u, 193183u}, // The Purple Princess
{175080u, 193182u}, // The Iron Eagle
{181689u, 193183u}, // Cloudkisser
{186238u, 193182u}, // The Mighty Wind
{181688u, 176083u}, // Northspear (icebreaker)
{190536u, 176084u}, // Stormwind's Pride (icebreaker)
};
auto itMapped = kEntryRemap.find(entry);
if (itMapped != kEntryRemap.end() && isUsableMovingPath(itMapped->second)) {
return itMapped->second;
}
// Fallback by display model family.
const bool looksLikeShip =
(displayId == 3015u || displayId == 2454u || displayId == 7446u || displayId == 455u || displayId == 462u);
const bool looksLikeZeppelin =
(displayId == 3031u || displayId == 7546u || displayId == 1587u || displayId == 807u || displayId == 808u);
if (looksLikeShip) {
static const uint32_t kShipCandidates[] = {176080u, 176081u, 176082u, 176083u, 176084u, 176085u, 194675u};
for (uint32_t id : kShipCandidates) {
if (isUsableMovingPath(id)) return id;
}
}
if (looksLikeZeppelin) {
static const uint32_t kZeppelinCandidates[] = {193182u, 193183u, 188360u, 190587u};
for (uint32_t id : kZeppelinCandidates) {
if (isUsableMovingPath(id)) return id;
}
}
// Last-resort: pick any moving DBC path so transport does not remain stationary.
for (const auto& [pathId, path] : paths_) {
if (path.fromDBC && !path.zOnly && path.durationMs > 0 && path.points.size() > 1) {
return pathId;
}
}
return 0;
} }
} // namespace wowee::game } // namespace wowee::game

View file

@ -565,23 +565,8 @@ network::Packet MovementPacket::build(Opcode opcode, const MovementInfo& info, u
// Write orientation // Write orientation
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.orientation), sizeof(float)); packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.orientation), sizeof(float));
// Write pitch if swimming/flying // Write transport data if on transport.
if (info.hasFlag(MovementFlags::SWIMMING) || info.hasFlag(MovementFlags::FLYING)) { // 3.3.5a ordering: transport block appears before pitch/fall/jump.
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.pitch), sizeof(float));
}
// Fall time is ALWAYS present in the packet (server reads it unconditionally).
// Jump velocity/angle data is only present when FALLING flag is set.
packet.writeUInt32(info.fallTime);
if (info.hasFlag(MovementFlags::FALLING)) {
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.jumpVelocity), sizeof(float));
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.jumpSinAngle), sizeof(float));
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.jumpCosAngle), sizeof(float));
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.jumpXYSpeed), sizeof(float));
}
// Write transport data if on transport
if (info.hasFlag(MovementFlags::ONTRANSPORT)) { if (info.hasFlag(MovementFlags::ONTRANSPORT)) {
// Write packed transport GUID // Write packed transport GUID
uint8_t transMask = 0; uint8_t transMask = 0;
@ -607,6 +592,30 @@ network::Packet MovementPacket::build(Opcode opcode, const MovementInfo& info, u
// Write transport time // Write transport time
packet.writeUInt32(info.transportTime); packet.writeUInt32(info.transportTime);
// Transport seat is always present in ONTRANSPORT movement info.
packet.writeUInt8(static_cast<uint8_t>(info.transportSeat));
// Optional second transport time for interpolated movement.
if (info.flags2 & 0x0200) {
packet.writeUInt32(info.transportTime2);
}
}
// Write pitch if swimming/flying
if (info.hasFlag(MovementFlags::SWIMMING) || info.hasFlag(MovementFlags::FLYING)) {
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.pitch), sizeof(float));
}
// Fall time is ALWAYS present in the packet (server reads it unconditionally).
// Jump velocity/angle data is only present when FALLING flag is set.
packet.writeUInt32(info.fallTime);
if (info.hasFlag(MovementFlags::FALLING)) {
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.jumpVelocity), sizeof(float));
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.jumpSinAngle), sizeof(float));
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.jumpCosAngle), sizeof(float));
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.jumpXYSpeed), sizeof(float));
} }
// Detailed hex dump for debugging // Detailed hex dump for debugging
@ -817,15 +826,18 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock
block.x = packet.readFloat(); block.x = packet.readFloat();
block.y = packet.readFloat(); block.y = packet.readFloat();
block.z = packet.readFloat(); block.z = packet.readFloat();
/*float transportOffsetX =*/ packet.readFloat(); block.onTransport = (transportGuid != 0);
/*float transportOffsetY =*/ packet.readFloat(); block.transportGuid = transportGuid;
/*float transportOffsetZ =*/ packet.readFloat(); block.transportX = packet.readFloat();
block.transportY = packet.readFloat();
block.transportZ = packet.readFloat();
block.orientation = packet.readFloat(); block.orientation = packet.readFloat();
/*float corpseOrientation =*/ packet.readFloat(); /*float corpseOrientation =*/ packet.readFloat();
block.hasMovement = true; block.hasMovement = true;
LOG_INFO(" TRANSPORT POSITION UPDATE: guid=0x", std::hex, transportGuid, std::dec, LOG_INFO(" TRANSPORT POSITION UPDATE: guid=0x", std::hex, transportGuid, std::dec,
" pos=(", block.x, ", ", block.y, ", ", block.z, "), o=", block.orientation); " pos=(", block.x, ", ", block.y, ", ", block.z, "), o=", block.orientation,
" offset=(", block.transportX, ", ", block.transportY, ", ", block.transportZ, ")");
} }
else if (updateFlags & UPDATEFLAG_STATIONARY_POSITION) { else if (updateFlags & UPDATEFLAG_STATIONARY_POSITION) {
// Simple stationary position (4 floats) // Simple stationary position (4 floats)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.