mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix empty chat messages and /dismount on Classic/Turtle
Chat: renderTextWithLinks now properly handles |cAARRGGBB color codes that aren't item links (e.g. colored player names), rendering the text in the specified color instead of discarding it. Dismount: Classic/Vanilla lacks CMSG_CANCEL_MOUNT_AURA (TBC+ opcode). Track mount aura spell ID when mountDisplayId changes, then use CMSG_CANCEL_AURA as fallback on expansions without the dedicated opcode.
This commit is contained in:
parent
8282583b9a
commit
f6a0be6a08
3 changed files with 102 additions and 9 deletions
|
|
@ -1401,6 +1401,7 @@ private:
|
|||
TaxiOrientationCallback taxiOrientationCallback_;
|
||||
TaxiFlightStartCallback taxiFlightStartCallback_;
|
||||
uint32_t currentMountDisplayId_ = 0;
|
||||
uint32_t mountAuraSpellId_ = 0; // Spell ID of the aura that caused mounting (for CMSG_CANCEL_AURA fallback)
|
||||
float serverRunSpeed_ = 7.0f;
|
||||
bool playerDead_ = false;
|
||||
bool releasedSpirit_ = false;
|
||||
|
|
|
|||
|
|
@ -3122,7 +3122,18 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
uint32_t old = currentMountDisplayId_;
|
||||
currentMountDisplayId_ = val;
|
||||
if (val != old && mountCallback_) mountCallback_(val);
|
||||
if (old == 0 && val != 0) {
|
||||
// Just mounted — find the mount aura (indefinite duration, self-cast)
|
||||
mountAuraSpellId_ = 0;
|
||||
for (const auto& a : playerAuras) {
|
||||
if (!a.isEmpty() && a.maxDurationMs < 0 && a.casterGuid == playerGuid) {
|
||||
mountAuraSpellId_ = a.spellId;
|
||||
}
|
||||
}
|
||||
LOG_INFO("Mount detected: displayId=", val, " auraSpellId=", mountAuraSpellId_);
|
||||
}
|
||||
if (old != 0 && val == 0) {
|
||||
mountAuraSpellId_ = 0;
|
||||
for (auto& a : playerAuras)
|
||||
if (!a.isEmpty() && a.maxDurationMs < 0) a = AuraSlot{};
|
||||
}
|
||||
|
|
@ -3502,7 +3513,17 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
uint32_t old = currentMountDisplayId_;
|
||||
currentMountDisplayId_ = val;
|
||||
if (val != old && mountCallback_) mountCallback_(val);
|
||||
if (old == 0 && val != 0) {
|
||||
mountAuraSpellId_ = 0;
|
||||
for (const auto& a : playerAuras) {
|
||||
if (!a.isEmpty() && a.maxDurationMs < 0 && a.casterGuid == playerGuid) {
|
||||
mountAuraSpellId_ = a.spellId;
|
||||
}
|
||||
}
|
||||
LOG_INFO("Mount detected (values update): displayId=", val, " auraSpellId=", mountAuraSpellId_);
|
||||
}
|
||||
if (old != 0 && val == 0) {
|
||||
mountAuraSpellId_ = 0;
|
||||
for (auto& a : playerAuras)
|
||||
if (!a.isEmpty() && a.maxDurationMs < 0) a = AuraSlot{};
|
||||
}
|
||||
|
|
@ -5973,6 +5994,7 @@ void GameHandler::dismount() {
|
|||
if (!socket) return;
|
||||
// Clear local mount state immediately (optimistic dismount).
|
||||
// Server will confirm via SMSG_UPDATE_OBJECT with mountDisplayId=0.
|
||||
uint32_t savedMountAura = mountAuraSpellId_;
|
||||
if (currentMountDisplayId_ != 0 || taxiMountActive_) {
|
||||
if (mountCallback_) {
|
||||
mountCallback_(0);
|
||||
|
|
@ -5980,11 +6002,31 @@ void GameHandler::dismount() {
|
|||
currentMountDisplayId_ = 0;
|
||||
taxiMountActive_ = false;
|
||||
taxiMountDisplayId_ = 0;
|
||||
mountAuraSpellId_ = 0;
|
||||
LOG_INFO("Dismount: cleared local mount state");
|
||||
}
|
||||
network::Packet pkt(wireOpcode(Opcode::CMSG_CANCEL_MOUNT_AURA));
|
||||
socket->send(pkt);
|
||||
LOG_INFO("Sent CMSG_CANCEL_MOUNT_AURA");
|
||||
// CMSG_CANCEL_MOUNT_AURA exists in TBC+ (0x0375). Classic/Vanilla doesn't have it.
|
||||
uint16_t cancelMountWire = wireOpcode(Opcode::CMSG_CANCEL_MOUNT_AURA);
|
||||
if (cancelMountWire != 0xFFFF) {
|
||||
network::Packet pkt(cancelMountWire);
|
||||
socket->send(pkt);
|
||||
LOG_INFO("Sent CMSG_CANCEL_MOUNT_AURA");
|
||||
} else if (savedMountAura != 0) {
|
||||
// Fallback for Classic/Vanilla: cancel the mount aura by spell ID
|
||||
auto pkt = CancelAuraPacket::build(savedMountAura);
|
||||
socket->send(pkt);
|
||||
LOG_INFO("Sent CMSG_CANCEL_AURA (mount spell ", savedMountAura, ") — Classic fallback");
|
||||
} else {
|
||||
// No tracked mount aura — try cancelling all indefinite self-cast auras
|
||||
// (mount aura detection may have missed if aura arrived after mount field)
|
||||
for (const auto& a : playerAuras) {
|
||||
if (!a.isEmpty() && a.maxDurationMs < 0 && a.casterGuid == playerGuid) {
|
||||
auto pkt = CancelAuraPacket::build(a.spellId);
|
||||
socket->send(pkt);
|
||||
LOG_INFO("Sent CMSG_CANCEL_AURA (spell ", a.spellId, ") — brute force dismount");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameHandler::handleForceRunSpeedChange(network::Packet& packet) {
|
||||
|
|
@ -6623,6 +6665,17 @@ void GameHandler::handleAuraUpdate(network::Packet& packet, bool isAll) {
|
|||
}
|
||||
(*auraList)[slot] = aura;
|
||||
}
|
||||
|
||||
// If player is mounted but we haven't identified the mount aura yet,
|
||||
// check newly added auras (aura update may arrive after mountDisplayId)
|
||||
if (data.guid == playerGuid && currentMountDisplayId_ != 0 && mountAuraSpellId_ == 0) {
|
||||
for (const auto& [slot, aura] : data.updates) {
|
||||
if (!aura.isEmpty() && aura.maxDurationMs < 0 && aura.casterGuid == playerGuid) {
|
||||
mountAuraSpellId_ = aura.spellId;
|
||||
LOG_INFO("Mount aura detected from aura update: spellId=", aura.spellId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -770,12 +770,51 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Failed to parse as item link — render the |c literally and continue
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
ImGui::TextWrapped("|c");
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine(0, 0);
|
||||
pos = nextSpecial + 2;
|
||||
// Not an item link — treat as colored text: |cAARRGGBB...text...|r
|
||||
if (nextSpecial == linkStart && text.size() > linkStart + 10) {
|
||||
ImVec4 cColor = parseWowColor(text, linkStart);
|
||||
size_t textStart = linkStart + 10; // after |cAARRGGBB
|
||||
size_t resetPos2 = text.find("|r", textStart);
|
||||
std::string coloredText;
|
||||
if (resetPos2 != std::string::npos) {
|
||||
coloredText = text.substr(textStart, resetPos2 - textStart);
|
||||
pos = resetPos2 + 2; // skip |r
|
||||
} else {
|
||||
coloredText = text.substr(textStart);
|
||||
pos = text.size();
|
||||
}
|
||||
// Strip any remaining WoW markup from the colored segment
|
||||
// (e.g. |H...|h pairs that aren't item links)
|
||||
std::string clean;
|
||||
for (size_t i = 0; i < coloredText.size(); i++) {
|
||||
if (coloredText[i] == '|' && i + 1 < coloredText.size()) {
|
||||
char next = coloredText[i + 1];
|
||||
if (next == 'H') {
|
||||
// Skip |H...|h
|
||||
size_t hEnd = coloredText.find("|h", i + 2);
|
||||
if (hEnd != std::string::npos) { i = hEnd + 1; continue; }
|
||||
} else if (next == 'h') {
|
||||
i += 1; continue; // skip |h
|
||||
} else if (next == 'r') {
|
||||
i += 1; continue; // skip |r
|
||||
}
|
||||
}
|
||||
clean += coloredText[i];
|
||||
}
|
||||
if (!clean.empty()) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, cColor);
|
||||
ImGui::TextWrapped("%s", clean.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine(0, 0);
|
||||
}
|
||||
} else {
|
||||
// Bare |c without enough chars for color — render literally
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
ImGui::TextWrapped("|c");
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine(0, 0);
|
||||
pos = nextSpecial + 2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue