mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: replace page-text chat-dump with proper book/scroll window
handlePageTextQueryResponse() now collects pages into bookPages_ vector instead of dumping lines to system chat. Multi-page items (nextPageId != 0) are automatically chained by requesting subsequent pages. The book window opens automatically when pages arrive, shows formatted text in a parchment- styled ImGui window with Prev/Next page navigation and a Close button. SMSG_READ_ITEM_OK clears bookPages_ so each item read starts fresh; handleGameObjectPageText() does the same before querying the first page. Closes the long-standing issue where reading scrolls and tattered notes spammed many separate chat messages instead of showing a readable UI.
This commit is contained in:
parent
218d68e275
commit
5883654e1e
4 changed files with 114 additions and 11 deletions
|
|
@ -1462,6 +1462,13 @@ public:
|
|||
uint32_t getTempEnchantRemainingMs(uint32_t slot) const;
|
||||
static constexpr const char* kTempEnchantSlotNames[] = { "Main Hand", "Off Hand", "Ranged" };
|
||||
|
||||
// ---- Readable text (books / scrolls / notes) ----
|
||||
// Populated by handlePageTextQueryResponse(); multi-page items chain via nextPageId.
|
||||
struct BookPage { uint32_t pageId = 0; std::string text; };
|
||||
const std::vector<BookPage>& getBookPages() const { return bookPages_; }
|
||||
bool hasBookOpen() const { return !bookPages_.empty(); }
|
||||
void clearBook() { bookPages_.clear(); }
|
||||
|
||||
// Other player level-up callback — fires when another player gains a level
|
||||
using OtherPlayerLevelUpCallback = std::function<void(uint64_t guid, uint32_t newLevel)>;
|
||||
void setOtherPlayerLevelUpCallback(OtherPlayerLevelUpCallback cb) { otherPlayerLevelUpCallback_ = std::move(cb); }
|
||||
|
|
@ -2820,6 +2827,7 @@ private:
|
|||
LevelUpCallback levelUpCallback_;
|
||||
LevelUpDeltas lastLevelUpDeltas_;
|
||||
std::vector<TempEnchantTimer> tempEnchantTimers_;
|
||||
std::vector<BookPage> bookPages_; // pages collected for the current readable item
|
||||
OtherPlayerLevelUpCallback otherPlayerLevelUpCallback_;
|
||||
AchievementEarnedCallback achievementEarnedCallback_;
|
||||
AreaDiscoveryCallback areaDiscoveryCallback_;
|
||||
|
|
|
|||
|
|
@ -437,6 +437,11 @@ private:
|
|||
bool showInspectWindow_ = false;
|
||||
void renderInspectWindow(game::GameHandler& gameHandler);
|
||||
|
||||
// Readable text window (books / scrolls / notes)
|
||||
bool showBookWindow_ = false;
|
||||
int bookCurrentPage_ = 0;
|
||||
void renderBookWindow(game::GameHandler& gameHandler);
|
||||
|
||||
// Threat window
|
||||
bool showThreatWindow_ = false;
|
||||
void renderThreatWindow(game::GameHandler& gameHandler);
|
||||
|
|
|
|||
|
|
@ -6098,7 +6098,7 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
|
||||
// ---- Read item results ----
|
||||
case Opcode::SMSG_READ_ITEM_OK:
|
||||
addSystemChatMessage("You read the item.");
|
||||
bookPages_.clear(); // fresh book for this item read
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
case Opcode::SMSG_READ_ITEM_FAILED:
|
||||
|
|
@ -11327,6 +11327,7 @@ void GameHandler::handleGameObjectPageText(network::Packet& packet) {
|
|||
else if (info.type == 10) pageId = info.data[7];
|
||||
|
||||
if (pageId != 0 && socket && state == WorldState::IN_WORLD) {
|
||||
bookPages_.clear(); // start a fresh book for this interaction
|
||||
auto req = PageTextQueryPacket::build(pageId, guid);
|
||||
socket->send(req);
|
||||
return;
|
||||
|
|
@ -11341,19 +11342,31 @@ 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 (!data.isValid()) return;
|
||||
|
||||
// Append page if not already collected
|
||||
bool alreadyHave = false;
|
||||
for (const auto& bp : bookPages_) {
|
||||
if (bp.pageId == data.pageId) { alreadyHave = true; break; }
|
||||
}
|
||||
if (!wrote) {
|
||||
addSystemChatMessage(data.text);
|
||||
if (!alreadyHave) {
|
||||
bookPages_.push_back({data.pageId, data.text});
|
||||
}
|
||||
|
||||
// Follow the chain: if there's a next page we haven't fetched yet, request it
|
||||
if (data.nextPageId != 0) {
|
||||
bool nextHave = false;
|
||||
for (const auto& bp : bookPages_) {
|
||||
if (bp.pageId == data.nextPageId) { nextHave = true; break; }
|
||||
}
|
||||
if (!nextHave && socket && state == WorldState::IN_WORLD) {
|
||||
auto req = PageTextQueryPacket::build(data.nextPageId, playerGuid);
|
||||
socket->send(req);
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("handlePageTextQueryResponse: pageId=", data.pageId,
|
||||
" nextPage=", data.nextPageId,
|
||||
" totalPages=", bookPages_.size());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -711,6 +711,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderAchievementWindow(gameHandler);
|
||||
renderGmTicketWindow(gameHandler);
|
||||
renderInspectWindow(gameHandler);
|
||||
renderBookWindow(gameHandler);
|
||||
renderThreatWindow(gameHandler);
|
||||
renderBgScoreboard(gameHandler);
|
||||
// renderQuestMarkers(gameHandler); // Disabled - using 3D billboard markers now
|
||||
|
|
@ -20194,6 +20195,82 @@ void GameScreen::renderObjectiveTracker(game::GameHandler&) {
|
|||
// full-featured draggable tracker with context menus and item icons.
|
||||
}
|
||||
|
||||
// ─── Book / Scroll / Note Window ──────────────────────────────────────────────
|
||||
void GameScreen::renderBookWindow(game::GameHandler& gameHandler) {
|
||||
// Auto-open when new pages arrive
|
||||
if (gameHandler.hasBookOpen() && !showBookWindow_) {
|
||||
showBookWindow_ = true;
|
||||
bookCurrentPage_ = 0;
|
||||
}
|
||||
if (!showBookWindow_) return;
|
||||
|
||||
const auto& pages = gameHandler.getBookPages();
|
||||
if (pages.empty()) { showBookWindow_ = false; return; }
|
||||
|
||||
// Clamp page index
|
||||
if (bookCurrentPage_ < 0) bookCurrentPage_ = 0;
|
||||
if (bookCurrentPage_ >= static_cast<int>(pages.size()))
|
||||
bookCurrentPage_ = static_cast<int>(pages.size()) - 1;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(420, 340), ImGuiCond_Appearing);
|
||||
ImGui::SetNextWindowPos(ImVec2(400, 180), ImGuiCond_Appearing);
|
||||
|
||||
bool open = showBookWindow_;
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.12f, 0.09f, 0.06f, 0.98f));
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4(0.25f, 0.18f, 0.08f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.5f, 0.37f, 0.18f, 1.0f));
|
||||
|
||||
char title[64];
|
||||
if (pages.size() > 1)
|
||||
snprintf(title, sizeof(title), "Page %d / %d###BookWin",
|
||||
bookCurrentPage_ + 1, static_cast<int>(pages.size()));
|
||||
else
|
||||
snprintf(title, sizeof(title), "###BookWin");
|
||||
|
||||
if (ImGui::Begin(title, &open, ImGuiWindowFlags_NoCollapse)) {
|
||||
// Parchment text colour
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.85f, 0.78f, 0.62f, 1.0f));
|
||||
|
||||
const std::string& text = pages[bookCurrentPage_].text;
|
||||
// Use a child region with word-wrap
|
||||
ImGui::SetNextWindowContentSize(ImVec2(ImGui::GetContentRegionAvail().x, 0));
|
||||
if (ImGui::BeginChild("##BookText",
|
||||
ImVec2(0, ImGui::GetContentRegionAvail().y - 34),
|
||||
false, ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
ImGui::TextWrapped("%s", text.c_str());
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
// Navigation row
|
||||
ImGui::Separator();
|
||||
bool canPrev = (bookCurrentPage_ > 0);
|
||||
bool canNext = (bookCurrentPage_ < static_cast<int>(pages.size()) - 1);
|
||||
|
||||
if (!canPrev) ImGui::BeginDisabled();
|
||||
if (ImGui::Button("< Prev", ImVec2(80, 0))) bookCurrentPage_--;
|
||||
if (!canPrev) ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (!canNext) ImGui::BeginDisabled();
|
||||
if (ImGui::Button("Next >", ImVec2(80, 0))) bookCurrentPage_++;
|
||||
if (!canNext) ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60);
|
||||
if (ImGui::Button("Close", ImVec2(60, 0))) {
|
||||
open = false;
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (!open) {
|
||||
showBookWindow_ = false;
|
||||
gameHandler.clearBook();
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Inspect Window ───────────────────────────────────────────────────────────
|
||||
void GameScreen::renderInspectWindow(game::GameHandler& gameHandler) {
|
||||
if (!showInspectWindow_) return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue