mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Store and display achievement criteria progress from SMSG_CRITERIA_UPDATE
Track criteria progress (criteriaId → counter) from SMSG_CRITERIA_UPDATE and SMSG_ALL_ACHIEVEMENT_DATA. Add a Criteria tab to the achievement window showing live progress values alongside the existing Earned achievements tab.
This commit is contained in:
parent
920950dfbd
commit
46eb66b77f
3 changed files with 75 additions and 40 deletions
|
|
@ -1294,6 +1294,7 @@ public:
|
|||
using AchievementEarnedCallback = std::function<void(uint32_t achievementId, const std::string& name)>;
|
||||
void setAchievementEarnedCallback(AchievementEarnedCallback cb) { achievementEarnedCallback_ = std::move(cb); }
|
||||
const std::unordered_set<uint32_t>& getEarnedAchievements() const { return earnedAchievements_; }
|
||||
const std::unordered_map<uint32_t, uint64_t>& getCriteriaProgress() const { return criteriaProgress_; }
|
||||
/// Returns the name of an achievement by ID, or empty string if unknown.
|
||||
const std::string& getAchievementName(uint32_t id) const {
|
||||
auto it = achievementNameCache_.find(id);
|
||||
|
|
@ -2439,6 +2440,8 @@ private:
|
|||
void loadAchievementNameCache();
|
||||
// Set of achievement IDs earned by the player (populated from SMSG_ALL_ACHIEVEMENT_DATA)
|
||||
std::unordered_set<uint32_t> earnedAchievements_;
|
||||
// Criteria progress: criteriaId → current value (from SMSG_CRITERIA_UPDATE)
|
||||
std::unordered_map<uint32_t, uint64_t> criteriaProgress_;
|
||||
void handleAllAchievementData(network::Packet& packet);
|
||||
|
||||
// Area name cache (lazy-loaded from WorldMapArea.dbc; maps AreaTable ID → display name)
|
||||
|
|
|
|||
|
|
@ -4153,12 +4153,12 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
}
|
||||
case Opcode::SMSG_CRITERIA_UPDATE: {
|
||||
// uint32 criteriaId + uint64 progress + uint32 elapsedTime + uint32 creationTime
|
||||
// Achievement criteria progress (informational — no criteria UI yet).
|
||||
if (packet.getSize() - packet.getReadPos() >= 20) {
|
||||
uint32_t criteriaId = packet.readUInt32();
|
||||
uint64_t progress = packet.readUInt64();
|
||||
/*uint32_t elapsedTime =*/ packet.readUInt32();
|
||||
/*uint32_t createTime =*/ packet.readUInt32();
|
||||
packet.readUInt32(); // elapsedTime
|
||||
packet.readUInt32(); // creationTime
|
||||
criteriaProgress_[criteriaId] = progress;
|
||||
LOG_DEBUG("SMSG_CRITERIA_UPDATE: id=", criteriaId, " progress=", progress);
|
||||
}
|
||||
break;
|
||||
|
|
@ -20080,18 +20080,21 @@ void GameHandler::handleAllAchievementData(network::Packet& packet) {
|
|||
earnedAchievements_.insert(id);
|
||||
}
|
||||
|
||||
// Skip criteria block (id + uint64 counter + uint32 date + uint32 flags until 0xFFFFFFFF)
|
||||
// Parse criteria block: id + uint64 counter + uint32 date + uint32 flags, sentinel 0xFFFFFFFF
|
||||
criteriaProgress_.clear();
|
||||
while (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
uint32_t id = packet.readUInt32();
|
||||
if (id == 0xFFFFFFFF) break;
|
||||
// counter(8) + date(4) + unknown(4) = 16 bytes
|
||||
if (packet.getSize() - packet.getReadPos() < 16) break;
|
||||
packet.readUInt64(); // counter
|
||||
uint64_t counter = packet.readUInt64();
|
||||
packet.readUInt32(); // date
|
||||
packet.readUInt32(); // unknown / flags
|
||||
criteriaProgress_[id] = counter;
|
||||
}
|
||||
|
||||
LOG_INFO("SMSG_ALL_ACHIEVEMENT_DATA: loaded ", earnedAchievements_.size(), " earned achievements");
|
||||
LOG_INFO("SMSG_ALL_ACHIEVEMENT_DATA: loaded ", earnedAchievements_.size(),
|
||||
" achievements, ", criteriaProgress_.size(), " criteria");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -14416,54 +14416,83 @@ void GameScreen::renderAchievementWindow(game::GameHandler& gameHandler) {
|
|||
}
|
||||
|
||||
const auto& earned = gameHandler.getEarnedAchievements();
|
||||
ImGui::Text("Earned: %u", static_cast<unsigned>(earned.size()));
|
||||
ImGui::SameLine();
|
||||
const auto& criteria = gameHandler.getCriteriaProgress();
|
||||
|
||||
ImGui::SetNextItemWidth(180.0f);
|
||||
ImGui::InputText("##achsearch", achievementSearchBuf_, sizeof(achievementSearchBuf_));
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear")) achievementSearchBuf_[0] = '\0';
|
||||
ImGui::Separator();
|
||||
|
||||
if (earned.empty()) {
|
||||
ImGui::TextDisabled("No achievements earned yet.");
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::BeginChild("##achlist", ImVec2(0, 0), false);
|
||||
|
||||
std::string filter(achievementSearchBuf_);
|
||||
// lower-case filter for case-insensitive matching
|
||||
for (char& c : filter) c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
|
||||
|
||||
// Collect and sort ids for stable display
|
||||
std::vector<uint32_t> ids(earned.begin(), earned.end());
|
||||
std::sort(ids.begin(), ids.end());
|
||||
|
||||
for (uint32_t id : ids) {
|
||||
const std::string& name = gameHandler.getAchievementName(id);
|
||||
const std::string& display = name.empty() ? std::to_string(id) : name;
|
||||
|
||||
if (!filter.empty()) {
|
||||
std::string lower = display;
|
||||
for (char& c : lower) c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
|
||||
if (lower.find(filter) == std::string::npos) continue;
|
||||
if (ImGui::BeginTabBar("##achtabs")) {
|
||||
// --- Earned tab ---
|
||||
char earnedLabel[32];
|
||||
snprintf(earnedLabel, sizeof(earnedLabel), "Earned (%u)###earned", (unsigned)earned.size());
|
||||
if (ImGui::BeginTabItem(earnedLabel)) {
|
||||
if (earned.empty()) {
|
||||
ImGui::TextDisabled("No achievements earned yet.");
|
||||
} else {
|
||||
ImGui::BeginChild("##achlist", ImVec2(0, 0), false);
|
||||
std::vector<uint32_t> ids(earned.begin(), earned.end());
|
||||
std::sort(ids.begin(), ids.end());
|
||||
for (uint32_t id : ids) {
|
||||
const std::string& name = gameHandler.getAchievementName(id);
|
||||
const std::string& display = name.empty() ? std::to_string(id) : name;
|
||||
if (!filter.empty()) {
|
||||
std::string lower = display;
|
||||
for (char& c : lower) c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
|
||||
if (lower.find(filter) == std::string::npos) continue;
|
||||
}
|
||||
ImGui::PushID(static_cast<int>(id));
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.0f, 1.0f), "\xE2\x98\x85");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(display.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("ID: %u", id);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::PushID(static_cast<int>(id));
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.0f, 1.0f), "[Achievement]");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(display.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("ID: %u", id);
|
||||
if (!name.empty()) ImGui::TextDisabled("%s", name.c_str());
|
||||
ImGui::EndTooltip();
|
||||
// --- Criteria progress tab ---
|
||||
char critLabel[32];
|
||||
snprintf(critLabel, sizeof(critLabel), "Criteria (%u)###crit", (unsigned)criteria.size());
|
||||
if (ImGui::BeginTabItem(critLabel)) {
|
||||
if (criteria.empty()) {
|
||||
ImGui::TextDisabled("No criteria progress received yet.");
|
||||
} else {
|
||||
ImGui::BeginChild("##critlist", ImVec2(0, 0), false);
|
||||
// Sort criteria by id for stable display
|
||||
std::vector<std::pair<uint32_t, uint64_t>> clist(criteria.begin(), criteria.end());
|
||||
std::sort(clist.begin(), clist.end());
|
||||
for (const auto& [cid, cval] : clist) {
|
||||
std::string label = std::to_string(cid);
|
||||
if (!filter.empty()) {
|
||||
std::string lower = label;
|
||||
for (char& c : lower) c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
|
||||
if (lower.find(filter) == std::string::npos) continue;
|
||||
}
|
||||
ImGui::PushID(static_cast<int>(cid));
|
||||
ImGui::TextDisabled("Criteria %u:", cid);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%llu", static_cast<unsigned long long>(cval));
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue