mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
game,ui: add rest state tracking and rested XP bar overlay
- Track PLAYER_REST_STATE_EXPERIENCE update field for all expansions (WotLK=636, Classic=718, TBC=928, Turtle=718) - Set isResting_ flag from SMSG_SET_REST_START packet - XP bar shows rested bonus as a lighter purple overlay extending beyond the current fill to (currentXp + restedXp) position - Tooltip text changes to "%u / %u XP (+%u rested)" when bonus exists - "zzz" indicator shown at bar right edge while resting
This commit is contained in:
parent
0ea8e55ad4
commit
2a9d26e1ea
8 changed files with 49 additions and 8 deletions
|
|
@ -22,6 +22,7 @@
|
||||||
"PLAYER_BYTES_2": 192,
|
"PLAYER_BYTES_2": 192,
|
||||||
"PLAYER_XP": 716,
|
"PLAYER_XP": 716,
|
||||||
"PLAYER_NEXT_LEVEL_XP": 717,
|
"PLAYER_NEXT_LEVEL_XP": 717,
|
||||||
|
"PLAYER_REST_STATE_EXPERIENCE": 718,
|
||||||
"PLAYER_FIELD_COINAGE": 1176,
|
"PLAYER_FIELD_COINAGE": 1176,
|
||||||
"PLAYER_QUEST_LOG_START": 198,
|
"PLAYER_QUEST_LOG_START": 198,
|
||||||
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
|
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
"PLAYER_BYTES_2": 238,
|
"PLAYER_BYTES_2": 238,
|
||||||
"PLAYER_XP": 926,
|
"PLAYER_XP": 926,
|
||||||
"PLAYER_NEXT_LEVEL_XP": 927,
|
"PLAYER_NEXT_LEVEL_XP": 927,
|
||||||
|
"PLAYER_REST_STATE_EXPERIENCE": 928,
|
||||||
"PLAYER_FIELD_COINAGE": 1441,
|
"PLAYER_FIELD_COINAGE": 1441,
|
||||||
"PLAYER_QUEST_LOG_START": 244,
|
"PLAYER_QUEST_LOG_START": 244,
|
||||||
"PLAYER_FIELD_INV_SLOT_HEAD": 650,
|
"PLAYER_FIELD_INV_SLOT_HEAD": 650,
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
"PLAYER_BYTES_2": 192,
|
"PLAYER_BYTES_2": 192,
|
||||||
"PLAYER_XP": 716,
|
"PLAYER_XP": 716,
|
||||||
"PLAYER_NEXT_LEVEL_XP": 717,
|
"PLAYER_NEXT_LEVEL_XP": 717,
|
||||||
|
"PLAYER_REST_STATE_EXPERIENCE": 718,
|
||||||
"PLAYER_FIELD_COINAGE": 1176,
|
"PLAYER_FIELD_COINAGE": 1176,
|
||||||
"PLAYER_QUEST_LOG_START": 198,
|
"PLAYER_QUEST_LOG_START": 198,
|
||||||
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
|
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
|
||||||
|
|
@ -35,4 +36,4 @@
|
||||||
"ITEM_FIELD_STACK_COUNT": 14,
|
"ITEM_FIELD_STACK_COUNT": 14,
|
||||||
"CONTAINER_FIELD_NUM_SLOTS": 48,
|
"CONTAINER_FIELD_NUM_SLOTS": 48,
|
||||||
"CONTAINER_FIELD_SLOT_1": 50
|
"CONTAINER_FIELD_SLOT_1": 50
|
||||||
}
|
}
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
"PLAYER_BYTES_2": 154,
|
"PLAYER_BYTES_2": 154,
|
||||||
"PLAYER_XP": 634,
|
"PLAYER_XP": 634,
|
||||||
"PLAYER_NEXT_LEVEL_XP": 635,
|
"PLAYER_NEXT_LEVEL_XP": 635,
|
||||||
|
"PLAYER_REST_STATE_EXPERIENCE": 636,
|
||||||
"PLAYER_FIELD_COINAGE": 1170,
|
"PLAYER_FIELD_COINAGE": 1170,
|
||||||
"PLAYER_QUEST_LOG_START": 158,
|
"PLAYER_QUEST_LOG_START": 158,
|
||||||
"PLAYER_FIELD_INV_SLOT_HEAD": 324,
|
"PLAYER_FIELD_INV_SLOT_HEAD": 324,
|
||||||
|
|
|
||||||
|
|
@ -640,6 +640,8 @@ public:
|
||||||
// XP tracking
|
// XP tracking
|
||||||
uint32_t getPlayerXp() const { return playerXp_; }
|
uint32_t getPlayerXp() const { return playerXp_; }
|
||||||
uint32_t getPlayerNextLevelXp() const { return playerNextLevelXp_; }
|
uint32_t getPlayerNextLevelXp() const { return playerNextLevelXp_; }
|
||||||
|
uint32_t getPlayerRestedXp() const { return playerRestedXp_; }
|
||||||
|
bool isPlayerResting() const { return isResting_; }
|
||||||
uint32_t getPlayerLevel() const { return serverPlayerLevel_; }
|
uint32_t getPlayerLevel() const { return serverPlayerLevel_; }
|
||||||
const std::vector<uint32_t>& getPlayerExploredZoneMasks() const { return playerExploredZones_; }
|
const std::vector<uint32_t>& getPlayerExploredZoneMasks() const { return playerExploredZones_; }
|
||||||
bool hasPlayerExploredZoneMasks() const { return hasPlayerExploredZones_; }
|
bool hasPlayerExploredZoneMasks() const { return hasPlayerExploredZones_; }
|
||||||
|
|
@ -2199,6 +2201,8 @@ private:
|
||||||
// ---- XP tracking ----
|
// ---- XP tracking ----
|
||||||
uint32_t playerXp_ = 0;
|
uint32_t playerXp_ = 0;
|
||||||
uint32_t playerNextLevelXp_ = 0;
|
uint32_t playerNextLevelXp_ = 0;
|
||||||
|
uint32_t playerRestedXp_ = 0;
|
||||||
|
bool isResting_ = false;
|
||||||
uint32_t serverPlayerLevel_ = 1;
|
uint32_t serverPlayerLevel_ = 1;
|
||||||
static uint32_t xpForLevel(uint32_t level);
|
static uint32_t xpForLevel(uint32_t level);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ enum class UF : uint16_t {
|
||||||
PLAYER_BYTES_2,
|
PLAYER_BYTES_2,
|
||||||
PLAYER_XP,
|
PLAYER_XP,
|
||||||
PLAYER_NEXT_LEVEL_XP,
|
PLAYER_NEXT_LEVEL_XP,
|
||||||
|
PLAYER_REST_STATE_EXPERIENCE,
|
||||||
PLAYER_FIELD_COINAGE,
|
PLAYER_FIELD_COINAGE,
|
||||||
PLAYER_QUEST_LOG_START,
|
PLAYER_QUEST_LOG_START,
|
||||||
PLAYER_FIELD_INV_SLOT_HEAD,
|
PLAYER_FIELD_INV_SLOT_HEAD,
|
||||||
|
|
|
||||||
|
|
@ -4644,8 +4644,9 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
||||||
case Opcode::SMSG_SET_REST_START: {
|
case Opcode::SMSG_SET_REST_START: {
|
||||||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||||
uint32_t restTrigger = packet.readUInt32();
|
uint32_t restTrigger = packet.readUInt32();
|
||||||
addSystemChatMessage(restTrigger > 0 ? "You are now resting."
|
isResting_ = (restTrigger > 0);
|
||||||
: "You are no longer resting.");
|
addSystemChatMessage(isResting_ ? "You are now resting."
|
||||||
|
: "You are no longer resting.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -7835,6 +7836,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
||||||
bool slotsChanged = false;
|
bool slotsChanged = false;
|
||||||
const uint16_t ufPlayerXp = fieldIndex(UF::PLAYER_XP);
|
const uint16_t ufPlayerXp = fieldIndex(UF::PLAYER_XP);
|
||||||
const uint16_t ufPlayerNextXp = fieldIndex(UF::PLAYER_NEXT_LEVEL_XP);
|
const uint16_t ufPlayerNextXp = fieldIndex(UF::PLAYER_NEXT_LEVEL_XP);
|
||||||
|
const uint16_t ufPlayerRestedXp = fieldIndex(UF::PLAYER_REST_STATE_EXPERIENCE);
|
||||||
const uint16_t ufPlayerLevel = fieldIndex(UF::UNIT_FIELD_LEVEL);
|
const uint16_t ufPlayerLevel = fieldIndex(UF::UNIT_FIELD_LEVEL);
|
||||||
const uint16_t ufCoinage = fieldIndex(UF::PLAYER_FIELD_COINAGE);
|
const uint16_t ufCoinage = fieldIndex(UF::PLAYER_FIELD_COINAGE);
|
||||||
const uint16_t ufArmor = fieldIndex(UF::UNIT_FIELD_RESISTANCES);
|
const uint16_t ufArmor = fieldIndex(UF::UNIT_FIELD_RESISTANCES);
|
||||||
|
|
@ -7842,6 +7844,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
||||||
for (const auto& [key, val] : block.fields) {
|
for (const auto& [key, val] : block.fields) {
|
||||||
if (key == ufPlayerXp) { playerXp_ = val; }
|
if (key == ufPlayerXp) { playerXp_ = val; }
|
||||||
else if (key == ufPlayerNextXp) { playerNextLevelXp_ = val; }
|
else if (key == ufPlayerNextXp) { playerNextLevelXp_ = val; }
|
||||||
|
else if (ufPlayerRestedXp != 0xFFFF && key == ufPlayerRestedXp) { playerRestedXp_ = val; }
|
||||||
else if (key == ufPlayerLevel) {
|
else if (key == ufPlayerLevel) {
|
||||||
serverPlayerLevel_ = val;
|
serverPlayerLevel_ = val;
|
||||||
for (auto& ch : characters) {
|
for (auto& ch : characters) {
|
||||||
|
|
|
||||||
|
|
@ -4409,7 +4409,9 @@ void GameScreen::renderXpBar(game::GameHandler& gameHandler) {
|
||||||
uint32_t nextLevelXp = gameHandler.getPlayerNextLevelXp();
|
uint32_t nextLevelXp = gameHandler.getPlayerNextLevelXp();
|
||||||
if (nextLevelXp == 0) return; // No XP data yet (level 80 or not initialized)
|
if (nextLevelXp == 0) return; // No XP data yet (level 80 or not initialized)
|
||||||
|
|
||||||
uint32_t currentXp = gameHandler.getPlayerXp();
|
uint32_t currentXp = gameHandler.getPlayerXp();
|
||||||
|
uint32_t restedXp = gameHandler.getPlayerRestedXp();
|
||||||
|
bool isResting = gameHandler.isPlayerResting();
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = core::Application::getInstance().getWindow();
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
@ -4449,9 +4451,10 @@ void GameScreen::renderXpBar(game::GameHandler& gameHandler) {
|
||||||
ImVec2 barMax = ImVec2(barMin.x + barSize.x, barMin.y + barSize.y);
|
ImVec2 barMax = ImVec2(barMin.x + barSize.x, barMin.y + barSize.y);
|
||||||
auto* drawList = ImGui::GetWindowDrawList();
|
auto* drawList = ImGui::GetWindowDrawList();
|
||||||
|
|
||||||
ImU32 bg = IM_COL32(15, 15, 20, 220);
|
ImU32 bg = IM_COL32(15, 15, 20, 220);
|
||||||
ImU32 fg = IM_COL32(148, 51, 238, 255);
|
ImU32 fg = IM_COL32(148, 51, 238, 255);
|
||||||
ImU32 seg = IM_COL32(35, 35, 45, 255);
|
ImU32 fgRest = IM_COL32(200, 170, 255, 220); // lighter purple for rested portion
|
||||||
|
ImU32 seg = IM_COL32(35, 35, 45, 255);
|
||||||
drawList->AddRectFilled(barMin, barMax, bg, 2.0f);
|
drawList->AddRectFilled(barMin, barMax, bg, 2.0f);
|
||||||
drawList->AddRect(barMin, barMax, IM_COL32(80, 80, 90, 220), 2.0f);
|
drawList->AddRect(barMin, barMax, IM_COL32(80, 80, 90, 220), 2.0f);
|
||||||
|
|
||||||
|
|
@ -4460,6 +4463,19 @@ void GameScreen::renderXpBar(game::GameHandler& gameHandler) {
|
||||||
drawList->AddRectFilled(barMin, ImVec2(barMin.x + fillW, barMax.y), fg, 2.0f);
|
drawList->AddRectFilled(barMin, ImVec2(barMin.x + fillW, barMax.y), fg, 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rested XP overlay: draw from current XP fill to (currentXp + restedXp) fill
|
||||||
|
if (restedXp > 0) {
|
||||||
|
float restedEndPct = std::min(1.0f, static_cast<float>(currentXp + restedXp)
|
||||||
|
/ static_cast<float>(nextLevelXp));
|
||||||
|
float restedStartX = barMin.x + fillW;
|
||||||
|
float restedEndX = barMin.x + barSize.x * restedEndPct;
|
||||||
|
if (restedEndX > restedStartX) {
|
||||||
|
drawList->AddRectFilled(ImVec2(restedStartX, barMin.y),
|
||||||
|
ImVec2(restedEndX, barMax.y),
|
||||||
|
fgRest, 2.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const int segments = 20;
|
const int segments = 20;
|
||||||
float segW = barSize.x / static_cast<float>(segments);
|
float segW = barSize.x / static_cast<float>(segments);
|
||||||
for (int i = 1; i < segments; ++i) {
|
for (int i = 1; i < segments; ++i) {
|
||||||
|
|
@ -4467,8 +4483,21 @@ void GameScreen::renderXpBar(game::GameHandler& gameHandler) {
|
||||||
drawList->AddLine(ImVec2(x, barMin.y + 1.0f), ImVec2(x, barMax.y - 1.0f), seg, 1.0f);
|
drawList->AddLine(ImVec2(x, barMin.y + 1.0f), ImVec2(x, barMax.y - 1.0f), seg, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rest indicator "zzz" to the right of the bar when resting
|
||||||
|
if (isResting) {
|
||||||
|
const char* zzz = "zzz";
|
||||||
|
ImVec2 zSize = ImGui::CalcTextSize(zzz);
|
||||||
|
float zx = barMax.x - zSize.x - 4.0f;
|
||||||
|
float zy = barMin.y + (barSize.y - zSize.y) * 0.5f;
|
||||||
|
drawList->AddText(ImVec2(zx, zy), IM_COL32(180, 150, 255, 220), zzz);
|
||||||
|
}
|
||||||
|
|
||||||
char overlay[96];
|
char overlay[96];
|
||||||
snprintf(overlay, sizeof(overlay), "%u / %u XP", currentXp, nextLevelXp);
|
if (restedXp > 0) {
|
||||||
|
snprintf(overlay, sizeof(overlay), "%u / %u XP (+%u rested)", currentXp, nextLevelXp, restedXp);
|
||||||
|
} else {
|
||||||
|
snprintf(overlay, sizeof(overlay), "%u / %u XP", currentXp, nextLevelXp);
|
||||||
|
}
|
||||||
ImVec2 textSize = ImGui::CalcTextSize(overlay);
|
ImVec2 textSize = ImGui::CalcTextSize(overlay);
|
||||||
float tx = barMin.x + (barSize.x - textSize.x) * 0.5f;
|
float tx = barMin.x + (barSize.x - textSize.x) * 0.5f;
|
||||||
float ty = barMin.y + (barSize.y - textSize.y) * 0.5f;
|
float ty = barMin.y + (barSize.y - textSize.y) * 0.5f;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue