mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 15:20:15 +00:00
Fix transport update handling, add desktop/icon resources, and clean repo artifacts
This commit is contained in:
parent
0a51ec8dda
commit
c20d5441d0
29 changed files with 284 additions and 41 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -67,3 +67,4 @@ cache/
|
|||
|
||||
# Single-player saves
|
||||
saves/
|
||||
wowee_[0-9][0-9][0-9][0-9]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(wowee VERSION 1.0.0 LANGUAGES CXX)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
|
@ -264,8 +265,13 @@ set(WOWEE_HEADERS
|
|||
include/ui/talent_screen.hpp
|
||||
)
|
||||
|
||||
set(WOWEE_PLATFORM_SOURCES)
|
||||
if(WIN32)
|
||||
list(APPEND WOWEE_PLATFORM_SOURCES resources/wowee.rc)
|
||||
endif()
|
||||
|
||||
# Create executable
|
||||
add_executable(wowee ${WOWEE_SOURCES} ${WOWEE_HEADERS})
|
||||
add_executable(wowee ${WOWEE_SOURCES} ${WOWEE_HEADERS} ${WOWEE_PLATFORM_SOURCES})
|
||||
|
||||
# Include directories
|
||||
target_include_directories(wowee PRIVATE
|
||||
|
|
@ -341,6 +347,22 @@ install(TARGETS wowee
|
|||
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
|
||||
message(STATUS "")
|
||||
message(STATUS "Wowee Configuration:")
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ struct TransportPath {
|
|||
bool looping; // Set to false after adding explicit wrap point
|
||||
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 fromDBC; // True if loaded from TransportAnimation.dbc, false for runtime fallback/custom paths
|
||||
};
|
||||
|
||||
struct ActiveTransport {
|
||||
|
|
@ -85,6 +86,15 @@ public:
|
|||
// Check if a path exists for a given GameObject entry
|
||||
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)
|
||||
void updateServerTransport(uint64_t guid, const glm::vec3& position, float orientation);
|
||||
|
||||
|
|
|
|||
|
|
@ -418,6 +418,8 @@ struct MovementInfo {
|
|||
float transportZ = 0.0f;
|
||||
float transportO = 0.0f; // Local orientation on transport
|
||||
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 {
|
||||
return (flags & static_cast<uint32_t>(flag)) != 0;
|
||||
|
|
|
|||
8
resources/wowee.desktop.in
Normal file
8
resources/wowee.desktop.in
Normal 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
1
resources/wowee.rc
Normal file
|
|
@ -0,0 +1 @@
|
|||
IDI_APP_ICON ICON "assets\\wowee.ico"
|
||||
|
|
@ -888,14 +888,28 @@ void Application::setupUICallbacks() {
|
|||
// Coordinates are already canonical (converted in game_handler.cpp when entity was created)
|
||||
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)) {
|
||||
LOG_WARNING("No TransportAnimation.dbc path for entry ", entry,
|
||||
" - transport will be stationary");
|
||||
uint32_t remappedPath = transportManager->pickFallbackMovingPath(entry, displayId);
|
||||
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)
|
||||
std::vector<glm::vec3> path = { canonicalSpawnPos };
|
||||
transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
|
||||
// Fallback: Stationary at spawn point (wait for server to send real position)
|
||||
std::vector<glm::vec3> path = { canonicalSpawnPos };
|
||||
transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
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)) {
|
||||
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);
|
||||
uint32_t remappedPath = transportManager->pickFallbackMovingPath(entry, displayId);
|
||||
if (remappedPath != 0) {
|
||||
pathId = remappedPath;
|
||||
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 {
|
||||
LOG_INFO("Auto-spawned transport with real path: entry=", entry,
|
||||
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
|
||||
|
|
|
|||
|
|
@ -1782,12 +1782,29 @@ void GameHandler::sendMovement(Opcode opcode) {
|
|||
movementInfo.transportX = playerTransportOffset_.x;
|
||||
movementInfo.transportY = playerTransportOffset_.y;
|
||||
movementInfo.transportZ = playerTransportOffset_.z;
|
||||
movementInfo.transportO = movementInfo.orientation; // Use same orientation as player
|
||||
movementInfo.transportTime = movementInfo.time; // Use same timestamp
|
||||
movementInfo.transportTime = movementInfo.time;
|
||||
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 {
|
||||
// Clear transport flag if not on transport
|
||||
movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::ONTRANSPORT);
|
||||
movementInfo.transportGuid = 0;
|
||||
movementInfo.transportSeat = -1;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Sending movement packet: opcode=0x", std::hex,
|
||||
|
|
@ -1907,6 +1924,26 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
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
|
||||
|
|
@ -2381,6 +2418,26 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
glm::vec3 pos = core::coords::serverToCanonical(glm::vec3(block.x, block.y, block.z));
|
||||
entity->setPosition(pos.x, pos.y, pos.z, block.orientation);
|
||||
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) {
|
||||
movementInfo.x = pos.x;
|
||||
movementInfo.y = pos.y;
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ void TransportManager::loadPathFromNodes(uint32_t pathId, const std::vector<glm:
|
|||
TransportPath path;
|
||||
path.pathId = pathId;
|
||||
path.zOnly = false; // Manually loaded paths are assumed to have XY movement
|
||||
path.fromDBC = false;
|
||||
|
||||
// Helper: compute segment duration from distance and speed
|
||||
auto segMsFromDist = [&](float dist) -> uint32_t {
|
||||
|
|
@ -237,12 +238,21 @@ void TransportManager::updateTransportMovement(ActiveTransport& transport, float
|
|||
transport.localClockMs += (uint32_t)(deltaTime * 1000.0f);
|
||||
pathTimeMs = transport.localClockMs % path.durationMs;
|
||||
} else {
|
||||
// Server-driven but no clock yet - don't move
|
||||
updateTransformMatrices(transport);
|
||||
if (wmoRenderer_) {
|
||||
wmoRenderer_->setInstanceTransform(transport.wmoInstanceId, transport.transform);
|
||||
// Server-driven but no clock yet. If updates never arrive, fall back to local animation.
|
||||
constexpr float kMissingUpdateFallbackSec = 2.5f;
|
||||
if ((elapsedTime_ - transport.lastServerUpdate) >= kMissingUpdateFallbackSec) {
|
||||
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)
|
||||
|
|
@ -496,6 +506,7 @@ void TransportManager::updateServerTransport(uint64_t guid, const glm::vec3& pos
|
|||
// Track server updates
|
||||
transport->serverUpdateCount++;
|
||||
transport->lastServerUpdate = elapsedTime_;
|
||||
transport->useClientAnimation = false; // Server updates take precedence
|
||||
|
||||
auto pathIt = paths_.find(transport->pathId);
|
||||
if (pathIt == paths_.end() || pathIt->second.durationMs == 0) {
|
||||
|
|
@ -836,6 +847,7 @@ bool TransportManager::loadTransportAnimationDBC(pipeline::AssetManager* assetMg
|
|||
path.looping = false;
|
||||
path.durationMs = durationMs;
|
||||
path.zOnly = isZOnly;
|
||||
path.fromDBC = true;
|
||||
paths_[transportEntry] = path;
|
||||
pathsLoaded++;
|
||||
|
||||
|
|
@ -857,7 +869,95 @@ bool TransportManager::loadTransportAnimationDBC(pipeline::AssetManager* assetMg
|
|||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -565,23 +565,8 @@ network::Packet MovementPacket::build(Opcode opcode, const MovementInfo& info, u
|
|||
// Write orientation
|
||||
packet.writeBytes(reinterpret_cast<const uint8_t*>(&info.orientation), sizeof(float));
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// Write transport data if on transport
|
||||
// Write transport data if on transport.
|
||||
// 3.3.5a ordering: transport block appears before pitch/fall/jump.
|
||||
if (info.hasFlag(MovementFlags::ONTRANSPORT)) {
|
||||
// Write packed transport GUID
|
||||
uint8_t transMask = 0;
|
||||
|
|
@ -607,6 +592,30 @@ network::Packet MovementPacket::build(Opcode opcode, const MovementInfo& info, u
|
|||
|
||||
// Write transport time
|
||||
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
|
||||
|
|
@ -817,15 +826,18 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock
|
|||
block.x = packet.readFloat();
|
||||
block.y = packet.readFloat();
|
||||
block.z = packet.readFloat();
|
||||
/*float transportOffsetX =*/ packet.readFloat();
|
||||
/*float transportOffsetY =*/ packet.readFloat();
|
||||
/*float transportOffsetZ =*/ packet.readFloat();
|
||||
block.onTransport = (transportGuid != 0);
|
||||
block.transportGuid = transportGuid;
|
||||
block.transportX = packet.readFloat();
|
||||
block.transportY = packet.readFloat();
|
||||
block.transportZ = packet.readFloat();
|
||||
block.orientation = packet.readFloat();
|
||||
/*float corpseOrientation =*/ packet.readFloat();
|
||||
block.hasMovement = true;
|
||||
|
||||
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) {
|
||||
// Simple stationary position (4 floats)
|
||||
|
|
|
|||
BIN
wowee_1746
BIN
wowee_1746
Binary file not shown.
BIN
wowee_1748
BIN
wowee_1748
Binary file not shown.
BIN
wowee_1752
BIN
wowee_1752
Binary file not shown.
BIN
wowee_1753
BIN
wowee_1753
Binary file not shown.
BIN
wowee_1804
BIN
wowee_1804
Binary file not shown.
BIN
wowee_1810
BIN
wowee_1810
Binary file not shown.
BIN
wowee_1814
BIN
wowee_1814
Binary file not shown.
BIN
wowee_1816
BIN
wowee_1816
Binary file not shown.
BIN
wowee_1819
BIN
wowee_1819
Binary file not shown.
BIN
wowee_1821
BIN
wowee_1821
Binary file not shown.
BIN
wowee_1823
BIN
wowee_1823
Binary file not shown.
BIN
wowee_1824
BIN
wowee_1824
Binary file not shown.
BIN
wowee_1825
BIN
wowee_1825
Binary file not shown.
BIN
wowee_1828
BIN
wowee_1828
Binary file not shown.
BIN
wowee_1834
BIN
wowee_1834
Binary file not shown.
BIN
wowee_1836
BIN
wowee_1836
Binary file not shown.
BIN
wowee_1840
BIN
wowee_1840
Binary file not shown.
BIN
wowee_1842
BIN
wowee_1842
Binary file not shown.
BIN
wowee_1844
BIN
wowee_1844
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue