mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix street sign interaction text and M2 sign orientation
Add page-text support for sign-like gameobject interactions by handling SMSG_GAMEOBJECT_PAGETEXT and SMSG_PAGE_TEXT_QUERY_RESPONSE, and issuing CMSG_PAGE_TEXT_QUERY when page IDs are available from cached GO template data. Normalize received page text tokens before chat display and add a fallback for basic signpost GO type clicks to print sign names when no page data is present. Correct M2 gameobject yaw alignment for signposts/arrows by applying render-space -90deg offset consistently across spawn, position update, and move-callback transforms; keep WMO orientation path unchanged.
This commit is contained in:
parent
ace24e8ccc
commit
c04e97e375
5 changed files with 118 additions and 5 deletions
|
|
@ -1052,6 +1052,8 @@ private:
|
|||
void handleNameQueryResponse(network::Packet& packet);
|
||||
void handleCreatureQueryResponse(network::Packet& packet);
|
||||
void handleGameObjectQueryResponse(network::Packet& packet);
|
||||
void handleGameObjectPageText(network::Packet& packet);
|
||||
void handlePageTextQueryResponse(network::Packet& packet);
|
||||
void handleItemQueryResponse(network::Packet& packet);
|
||||
void handleInspectResults(network::Packet& packet);
|
||||
void queryItemInfo(uint32_t entry, uint64_t guid);
|
||||
|
|
|
|||
|
|
@ -1405,6 +1405,27 @@ public:
|
|||
static bool parse(network::Packet& packet, GameObjectQueryResponseData& data);
|
||||
};
|
||||
|
||||
/** CMSG_PAGE_TEXT_QUERY packet builder */
|
||||
class PageTextQueryPacket {
|
||||
public:
|
||||
static network::Packet build(uint32_t pageId, uint64_t guid);
|
||||
};
|
||||
|
||||
/** SMSG_PAGE_TEXT_QUERY_RESPONSE data */
|
||||
struct PageTextQueryResponseData {
|
||||
uint32_t pageId = 0;
|
||||
std::string text;
|
||||
uint32_t nextPageId = 0;
|
||||
|
||||
bool isValid() const { return pageId != 0; }
|
||||
};
|
||||
|
||||
/** SMSG_PAGE_TEXT_QUERY_RESPONSE parser */
|
||||
class PageTextQueryResponseParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, PageTextQueryResponseData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Item Query
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -1720,7 +1720,7 @@ void Application::setupUICallbacks() {
|
|||
if (auto* mr = renderer->getM2Renderer()) {
|
||||
glm::mat4 transform(1.0f);
|
||||
transform = glm::translate(transform, renderPos);
|
||||
transform = glm::rotate(transform, orientation, glm::vec3(0, 0, 1));
|
||||
transform = glm::rotate(transform, orientation - glm::radians(90.0f), glm::vec3(0, 0, 1));
|
||||
mr->setInstanceTransform(info.instanceId, transform);
|
||||
}
|
||||
}
|
||||
|
|
@ -5420,7 +5420,9 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t
|
|||
if (auto* mr = renderer->getM2Renderer()) {
|
||||
glm::mat4 transform(1.0f);
|
||||
transform = glm::translate(transform, renderPos);
|
||||
transform = glm::rotate(transform, orientation, glm::vec3(0, 0, 1));
|
||||
// M2 gameobjects use model-forward alignment like character M2s.
|
||||
// Apply -90deg in render space to match world-facing orientation.
|
||||
transform = glm::rotate(transform, orientation - glm::radians(90.0f), glm::vec3(0, 0, 1));
|
||||
mr->setInstanceTransform(info.instanceId, transform);
|
||||
}
|
||||
}
|
||||
|
|
@ -5474,7 +5476,8 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t
|
|||
bool isWmo = lowerPath.size() >= 4 && lowerPath.substr(lowerPath.size() - 4) == ".wmo";
|
||||
|
||||
glm::vec3 renderPos = core::coords::canonicalToRender(glm::vec3(x, y, z));
|
||||
float renderYaw = orientation;
|
||||
const float renderYawWmo = orientation;
|
||||
const float renderYawM2 = orientation - glm::radians(90.0f);
|
||||
|
||||
bool loadedAsWmo = false;
|
||||
if (isWmo) {
|
||||
|
|
@ -5545,7 +5548,7 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t
|
|||
|
||||
if (loadedAsWmo) {
|
||||
uint32_t instanceId = wmoRenderer->createInstance(modelId, renderPos,
|
||||
glm::vec3(0.0f, 0.0f, renderYaw), 1.0f);
|
||||
glm::vec3(0.0f, 0.0f, renderYawWmo), 1.0f);
|
||||
if (instanceId == 0) {
|
||||
LOG_WARNING("Failed to create gameobject WMO instance for guid 0x", std::hex, guid, std::dec);
|
||||
return;
|
||||
|
|
@ -5640,7 +5643,7 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t
|
|||
}
|
||||
|
||||
uint32_t instanceId = m2Renderer->createInstance(modelId, renderPos,
|
||||
glm::vec3(0.0f, 0.0f, renderYaw), 1.0f);
|
||||
glm::vec3(0.0f, 0.0f, renderYawM2), 1.0f);
|
||||
if (instanceId == 0) {
|
||||
LOG_WARNING("Failed to create gameobject instance for guid 0x", std::hex, guid, std::dec);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -2281,6 +2281,12 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
case Opcode::SMSG_GAMEOBJECT_QUERY_RESPONSE:
|
||||
handleGameObjectQueryResponse(packet);
|
||||
break;
|
||||
case Opcode::SMSG_GAMEOBJECT_PAGETEXT:
|
||||
handleGameObjectPageText(packet);
|
||||
break;
|
||||
case Opcode::SMSG_PAGE_TEXT_QUERY_RESPONSE:
|
||||
handlePageTextQueryResponse(packet);
|
||||
break;
|
||||
case Opcode::SMSG_QUESTGIVER_STATUS: {
|
||||
if (packet.getSize() - packet.getReadPos() >= 9) {
|
||||
uint64_t npcGuid = packet.readUInt64();
|
||||
|
|
@ -7203,6 +7209,60 @@ void GameHandler::handleGameObjectQueryResponse(network::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
void GameHandler::handleGameObjectPageText(network::Packet& packet) {
|
||||
if (packet.getSize() - packet.getReadPos() < 8) return;
|
||||
uint64_t guid = packet.readUInt64();
|
||||
auto entity = entityManager.getEntity(guid);
|
||||
if (!entity || entity->getType() != ObjectType::GAMEOBJECT) return;
|
||||
|
||||
auto go = std::static_pointer_cast<GameObject>(entity);
|
||||
uint32_t entry = go->getEntry();
|
||||
if (entry == 0) return;
|
||||
|
||||
auto cacheIt = gameObjectInfoCache_.find(entry);
|
||||
if (cacheIt == gameObjectInfoCache_.end()) {
|
||||
queryGameObjectInfo(entry, guid);
|
||||
return;
|
||||
}
|
||||
|
||||
const GameObjectQueryResponseData& info = cacheIt->second;
|
||||
uint32_t pageId = 0;
|
||||
// AzerothCore layout:
|
||||
// type 9 (TEXT): data[0]=pageID
|
||||
// type 10 (GOOBER): data[7]=pageId
|
||||
if (info.type == 9) pageId = info.data[0];
|
||||
else if (info.type == 10) pageId = info.data[7];
|
||||
|
||||
if (pageId != 0 && socket && state == WorldState::IN_WORLD) {
|
||||
auto req = PageTextQueryPacket::build(pageId, guid);
|
||||
socket->send(req);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info.name.empty()) {
|
||||
addSystemChatMessage(info.name);
|
||||
}
|
||||
}
|
||||
|
||||
void GameHandler::handlePageTextQueryResponse(network::Packet& packet) {
|
||||
PageTextQueryResponseData data;
|
||||
if (!PageTextQueryResponseParser::parse(packet, data)) return;
|
||||
|
||||
if (!data.text.empty()) {
|
||||
std::istringstream iss(data.text);
|
||||
std::string line;
|
||||
bool wrote = false;
|
||||
while (std::getline(iss, line)) {
|
||||
if (line.empty()) continue;
|
||||
addSystemChatMessage(line);
|
||||
wrote = true;
|
||||
}
|
||||
if (!wrote) {
|
||||
addSystemChatMessage(data.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Item Query
|
||||
// ============================================================
|
||||
|
|
@ -9730,6 +9790,14 @@ void GameHandler::performGameObjectInteractionNow(uint64_t guid) {
|
|||
goEntry = go->getEntry();
|
||||
goName = go->getName();
|
||||
if (auto* info = getCachedGameObjectInfo(goEntry)) goType = info->type;
|
||||
if (goType == 5 && !goName.empty()) {
|
||||
std::string lower = goName;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
if (lower.rfind("doodad_", 0) != 0) {
|
||||
addSystemChatMessage(goName);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Face object and send heartbeat before use so strict servers don't require
|
||||
// a nudge movement to accept interaction.
|
||||
|
|
|
|||
|
|
@ -2089,6 +2089,25 @@ bool GameObjectQueryResponseParser::parse(network::Packet& packet, GameObjectQue
|
|||
return true;
|
||||
}
|
||||
|
||||
network::Packet PageTextQueryPacket::build(uint32_t pageId, uint64_t guid) {
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_PAGE_TEXT_QUERY));
|
||||
packet.writeUInt32(pageId);
|
||||
packet.writeUInt64(guid);
|
||||
return packet;
|
||||
}
|
||||
|
||||
bool PageTextQueryResponseParser::parse(network::Packet& packet, PageTextQueryResponseData& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 4) return false;
|
||||
data.pageId = packet.readUInt32();
|
||||
data.text = normalizeWowTextTokens(packet.readString());
|
||||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
data.nextPageId = packet.readUInt32();
|
||||
} else {
|
||||
data.nextPageId = 0;
|
||||
}
|
||||
return data.isValid();
|
||||
}
|
||||
|
||||
// ---- Item Query ----
|
||||
|
||||
network::Packet ItemQueryPacket::build(uint32_t entry, uint64_t guid) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue