mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Add transport support, gameobject queries, and fix item use
- Add setInstancePosition() to M2Renderer and WMORenderer for moving transport instances at runtime - Detect UPDATEFLAG_TRANSPORT on gameobjects and track transport GUIDs - Parse player-on-transport state from movement blocks - Wire transport move callback in Application to update render positions - Implement CMSG_GAMEOBJECT_QUERY / SMSG_GAMEOBJECT_QUERY_RESPONSE so gameobjects display proper names instead of "Unknown" - Add name/entry fields to GameObject entity class - Fix CMSG_USE_ITEM packet: remove extra uint8 that shifted the item GUID by one byte, breaking hearthstone and all item usage - Remove redundant CMSG_LOOT after CMSG_GAMEOBJECT_USE for chests - Show PvP enabled/disabled state in toggle message - Relax WMO ramp wall-collision step-up check to allow walking on gentle ramps where floor rise per step is under 0.1 units - Add M2 fallback when WMO group files fail to load for gameobjects - Handle re-creation of existing gameobject render instances by updating position instead of silently ignoring
This commit is contained in:
parent
5610faa958
commit
0ce38cfb99
12 changed files with 391 additions and 65 deletions
|
|
@ -662,9 +662,11 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
break;
|
||||
}
|
||||
case Opcode::SMSG_BUY_FAILED:
|
||||
case Opcode::SMSG_GAMEOBJECT_QUERY_RESPONSE:
|
||||
case Opcode::MSG_RAID_TARGET_UPDATE:
|
||||
break;
|
||||
case Opcode::SMSG_GAMEOBJECT_QUERY_RESPONSE:
|
||||
handleGameObjectQueryResponse(packet);
|
||||
break;
|
||||
case Opcode::SMSG_QUESTGIVER_STATUS: {
|
||||
// uint64 npcGuid + uint8 status
|
||||
if (packet.getSize() - packet.getReadPos() >= 9) {
|
||||
|
|
@ -731,9 +733,18 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
case Opcode::MSG_MOVE_TELEPORT_ACK:
|
||||
handleTeleportAck(packet);
|
||||
break;
|
||||
case Opcode::SMSG_TRANSFER_PENDING:
|
||||
LOG_INFO("SMSG_TRANSFER_PENDING received - map transfer incoming");
|
||||
case Opcode::SMSG_TRANSFER_PENDING: {
|
||||
// SMSG_TRANSFER_PENDING: uint32 mapId, then optional transport data
|
||||
uint32_t pendingMapId = packet.readUInt32();
|
||||
LOG_INFO("SMSG_TRANSFER_PENDING: mapId=", pendingMapId);
|
||||
// Optional: if remaining data, there's a transport entry + mapId
|
||||
if (packet.getReadPos() + 8 <= packet.getSize()) {
|
||||
uint32_t transportEntry = packet.readUInt32();
|
||||
uint32_t transportMapId = packet.readUInt32();
|
||||
LOG_INFO(" Transport entry=", transportEntry, " transportMapId=", transportMapId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- Taxi / Flight Paths ----
|
||||
case Opcode::SMSG_SHOWTAXINODES:
|
||||
|
|
@ -1356,6 +1367,11 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
gameObjectDespawnCallback_(guid);
|
||||
}
|
||||
}
|
||||
transportGuids_.erase(guid);
|
||||
if (playerTransportGuid_ == guid) {
|
||||
playerTransportGuid_ = 0;
|
||||
playerTransportOffset_ = glm::vec3(0.0f);
|
||||
}
|
||||
entityManager.removeEntity(guid);
|
||||
}
|
||||
}
|
||||
|
|
@ -1400,6 +1416,21 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
if (block.guid == playerGuid && block.runSpeed > 0.1f && block.runSpeed < 100.0f) {
|
||||
serverRunSpeed_ = block.runSpeed;
|
||||
}
|
||||
// Track player-on-transport state
|
||||
if (block.guid == playerGuid) {
|
||||
if (block.onTransport) {
|
||||
playerTransportGuid_ = block.transportGuid;
|
||||
playerTransportOffset_ = glm::vec3(block.transportX, block.transportY, block.transportZ);
|
||||
LOG_INFO("Player on transport: 0x", std::hex, playerTransportGuid_, std::dec,
|
||||
" offset=(", playerTransportOffset_.x, ", ", playerTransportOffset_.y, ", ", playerTransportOffset_.z, ")");
|
||||
} else {
|
||||
if (playerTransportGuid_ != 0) {
|
||||
LOG_INFO("Player left transport");
|
||||
}
|
||||
playerTransportGuid_ = 0;
|
||||
playerTransportOffset_ = glm::vec3(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set fields
|
||||
|
|
@ -1501,17 +1532,39 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Extract displayId for gameobjects (3.3.5a: GAMEOBJECT_DISPLAYID = field 8)
|
||||
// Extract displayId and entry for gameobjects (3.3.5a: GAMEOBJECT_DISPLAYID = field 8)
|
||||
if (block.objectType == ObjectType::GAMEOBJECT) {
|
||||
auto go = std::static_pointer_cast<GameObject>(entity);
|
||||
auto itDisp = block.fields.find(8);
|
||||
if (itDisp != block.fields.end()) {
|
||||
go->setDisplayId(itDisp->second);
|
||||
}
|
||||
// Extract entry and query name (OBJECT_FIELD_ENTRY = index 3)
|
||||
auto itEntry = block.fields.find(3);
|
||||
if (itEntry != block.fields.end() && itEntry->second != 0) {
|
||||
go->setEntry(itEntry->second);
|
||||
auto cacheIt = gameObjectInfoCache_.find(itEntry->second);
|
||||
if (cacheIt != gameObjectInfoCache_.end()) {
|
||||
go->setName(cacheIt->second.name);
|
||||
}
|
||||
queryGameObjectInfo(itEntry->second, block.guid);
|
||||
}
|
||||
// Detect transport GameObjects via UPDATEFLAG_TRANSPORT (0x0002)
|
||||
if (block.updateFlags & 0x0002) {
|
||||
transportGuids_.insert(block.guid);
|
||||
LOG_INFO("Detected transport GameObject: 0x", std::hex, block.guid, std::dec,
|
||||
" displayId=", go->getDisplayId(),
|
||||
" pos=(", go->getX(), ", ", go->getY(), ", ", go->getZ(), ")");
|
||||
}
|
||||
if (go->getDisplayId() != 0 && gameObjectSpawnCallback_) {
|
||||
gameObjectSpawnCallback_(block.guid, go->getDisplayId(),
|
||||
go->getX(), go->getY(), go->getZ(), go->getOrientation());
|
||||
}
|
||||
// Fire transport move callback for transports (position update on re-creation)
|
||||
if (transportGuids_.count(block.guid) && transportMoveCallback_) {
|
||||
transportMoveCallback_(block.guid,
|
||||
go->getX(), go->getY(), go->getZ(), go->getOrientation());
|
||||
}
|
||||
}
|
||||
// Track online item objects
|
||||
if (block.objectType == ObjectType::ITEM) {
|
||||
|
|
@ -1724,6 +1777,11 @@ 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);
|
||||
|
||||
// Fire transport move callback if this is a known transport
|
||||
if (transportGuids_.count(block.guid) && transportMoveCallback_) {
|
||||
transportMoveCallback_(block.guid, pos.x, pos.y, pos.z, block.orientation);
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING("MOVEMENT update for unknown entity: 0x", std::hex, block.guid, std::dec);
|
||||
}
|
||||
|
|
@ -2454,7 +2512,19 @@ void GameHandler::togglePvp() {
|
|||
|
||||
auto packet = TogglePvpPacket::build();
|
||||
socket->send(packet);
|
||||
addSystemChatMessage("PvP flag toggled.");
|
||||
// Check current PVP state from player's UNIT_FIELD_FLAGS (index 59)
|
||||
// UNIT_FLAG_PVP = 0x00001000
|
||||
auto entity = entityManager.getEntity(playerGuid);
|
||||
bool currentlyPvp = false;
|
||||
if (entity) {
|
||||
currentlyPvp = (entity->getField(59) & 0x00001000) != 0;
|
||||
}
|
||||
// We're toggling, so report the NEW state
|
||||
if (currentlyPvp) {
|
||||
addSystemChatMessage("PvP flag disabled.");
|
||||
} else {
|
||||
addSystemChatMessage("PvP flag enabled.");
|
||||
}
|
||||
LOG_INFO("Toggled PvP flag");
|
||||
}
|
||||
|
||||
|
|
@ -2939,6 +3009,15 @@ void GameHandler::queryCreatureInfo(uint32_t entry, uint64_t guid) {
|
|||
socket->send(packet);
|
||||
}
|
||||
|
||||
void GameHandler::queryGameObjectInfo(uint32_t entry, uint64_t guid) {
|
||||
if (gameObjectInfoCache_.count(entry) || pendingGameObjectQueries_.count(entry)) return;
|
||||
if (state != WorldState::IN_WORLD || !socket) return;
|
||||
|
||||
pendingGameObjectQueries_.insert(entry);
|
||||
auto packet = GameObjectQueryPacket::build(entry, guid);
|
||||
socket->send(packet);
|
||||
}
|
||||
|
||||
std::string GameHandler::getCachedPlayerName(uint64_t guid) const {
|
||||
auto it = playerNameCache.find(guid);
|
||||
return (it != playerNameCache.end()) ? it->second : "";
|
||||
|
|
@ -2986,6 +3065,30 @@ void GameHandler::handleCreatureQueryResponse(network::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// GameObject Query
|
||||
// ============================================================
|
||||
|
||||
void GameHandler::handleGameObjectQueryResponse(network::Packet& packet) {
|
||||
GameObjectQueryResponseData data;
|
||||
if (!GameObjectQueryResponseParser::parse(packet, data)) return;
|
||||
|
||||
pendingGameObjectQueries_.erase(data.entry);
|
||||
|
||||
if (data.isValid()) {
|
||||
gameObjectInfoCache_[data.entry] = data;
|
||||
// Update all gameobject entities with this entry
|
||||
for (auto& [guid, entity] : entityManager.getEntities()) {
|
||||
if (entity->getType() == ObjectType::GAMEOBJECT) {
|
||||
auto go = std::static_pointer_cast<GameObject>(entity);
|
||||
if (go->getEntry() == data.entry) {
|
||||
go->setName(data.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Item Query
|
||||
// ============================================================
|
||||
|
|
@ -3959,9 +4062,6 @@ void GameHandler::interactWithGameObject(uint64_t guid) {
|
|||
if (state != WorldState::IN_WORLD || !socket) return;
|
||||
auto packet = GameObjectUsePacket::build(guid);
|
||||
socket->send(packet);
|
||||
// Many lootable chests require a loot request after use.
|
||||
auto loot = LootPacket::build(guid);
|
||||
socket->send(loot);
|
||||
}
|
||||
|
||||
void GameHandler::selectGossipOption(uint32_t optionId) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue