mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: show stat gains in level-up toast from SMSG_LEVELUP_INFO
Parse hp/mana/str/agi/sta/int/spi deltas from SMSG_LEVELUP_INFO payload and display them in green below the "You have reached level X!" banner. Extends DING_DURATION to 4s to give players time to read the gains.
This commit is contained in:
parent
6df8c72cf7
commit
6957ba97ea
4 changed files with 80 additions and 9 deletions
|
|
@ -1438,6 +1438,14 @@ public:
|
|||
using LevelUpCallback = std::function<void(uint32_t newLevel)>;
|
||||
void setLevelUpCallback(LevelUpCallback cb) { levelUpCallback_ = std::move(cb); }
|
||||
|
||||
// Stat deltas from the last SMSG_LEVELUP_INFO (valid until next level-up)
|
||||
struct LevelUpDeltas {
|
||||
uint32_t hp = 0;
|
||||
uint32_t mana = 0;
|
||||
uint32_t str = 0, agi = 0, sta = 0, intel = 0, spi = 0;
|
||||
};
|
||||
const LevelUpDeltas& getLastLevelUpDeltas() const { return lastLevelUpDeltas_; }
|
||||
|
||||
// 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); }
|
||||
|
|
@ -2793,6 +2801,7 @@ private:
|
|||
NpcVendorCallback npcVendorCallback_;
|
||||
ChargeCallback chargeCallback_;
|
||||
LevelUpCallback levelUpCallback_;
|
||||
LevelUpDeltas lastLevelUpDeltas_;
|
||||
OtherPlayerLevelUpCallback otherPlayerLevelUpCallback_;
|
||||
AchievementEarnedCallback achievementEarnedCallback_;
|
||||
AreaDiscoveryCallback areaDiscoveryCallback_;
|
||||
|
|
|
|||
|
|
@ -511,9 +511,12 @@ private:
|
|||
bool leftClickWasPress_ = false;
|
||||
|
||||
// Level-up ding animation
|
||||
static constexpr float DING_DURATION = 3.0f;
|
||||
static constexpr float DING_DURATION = 4.0f;
|
||||
float dingTimer_ = 0.0f;
|
||||
uint32_t dingLevel_ = 0;
|
||||
uint32_t dingHpDelta_ = 0;
|
||||
uint32_t dingManaDelta_ = 0;
|
||||
uint32_t dingStats_[5] = {}; // str/agi/sta/int/spi deltas
|
||||
void renderDingEffect();
|
||||
|
||||
// Achievement toast banner
|
||||
|
|
@ -616,7 +619,9 @@ private:
|
|||
size_t dpsLogSeenCount_ = 0; // log entries already scanned
|
||||
|
||||
public:
|
||||
void triggerDing(uint32_t newLevel);
|
||||
void triggerDing(uint32_t newLevel, uint32_t hpDelta = 0, uint32_t manaDelta = 0,
|
||||
uint32_t str = 0, uint32_t agi = 0, uint32_t sta = 0,
|
||||
uint32_t intel = 0, uint32_t spi = 0);
|
||||
void triggerAchievementToast(uint32_t achievementId, std::string name = {});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3823,10 +3823,21 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
case Opcode::SMSG_LEVELUP_INFO:
|
||||
case Opcode::SMSG_LEVELUP_INFO_ALT: {
|
||||
// Server-authoritative level-up event.
|
||||
// First field is always the new level in Classic/TBC/WotLK-era layouts.
|
||||
// WotLK layout: uint32 newLevel + uint32 hpDelta + uint32 manaDelta + 5x uint32 statDeltas
|
||||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
uint32_t newLevel = packet.readUInt32();
|
||||
if (newLevel > 0) {
|
||||
// Parse stat deltas (WotLK layout has 7 more uint32s)
|
||||
lastLevelUpDeltas_ = {};
|
||||
if (packet.getSize() - packet.getReadPos() >= 28) {
|
||||
lastLevelUpDeltas_.hp = packet.readUInt32();
|
||||
lastLevelUpDeltas_.mana = packet.readUInt32();
|
||||
lastLevelUpDeltas_.str = packet.readUInt32();
|
||||
lastLevelUpDeltas_.agi = packet.readUInt32();
|
||||
lastLevelUpDeltas_.sta = packet.readUInt32();
|
||||
lastLevelUpDeltas_.intel = packet.readUInt32();
|
||||
lastLevelUpDeltas_.spi = packet.readUInt32();
|
||||
}
|
||||
uint32_t oldLevel = serverPlayerLevel_;
|
||||
serverPlayerLevel_ = std::max(serverPlayerLevel_, newLevel);
|
||||
for (auto& ch : characters) {
|
||||
|
|
@ -3840,7 +3851,6 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Remaining payload (hp/mana/stat deltas) is optional for our client.
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -284,10 +284,11 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
|
||||
// Set up level-up callback (once)
|
||||
if (!levelUpCallbackSet_) {
|
||||
gameHandler.setLevelUpCallback([this](uint32_t newLevel) {
|
||||
gameHandler.setLevelUpCallback([this, &gameHandler](uint32_t newLevel) {
|
||||
levelUpFlashAlpha_ = 1.0f;
|
||||
levelUpDisplayLevel_ = newLevel;
|
||||
triggerDing(newLevel);
|
||||
const auto& d = gameHandler.getLastLevelUpDeltas();
|
||||
triggerDing(newLevel, d.hp, d.mana, d.str, d.agi, d.sta, d.intel, d.spi);
|
||||
});
|
||||
levelUpCallbackSet_ = true;
|
||||
}
|
||||
|
|
@ -18058,9 +18059,18 @@ void GameScreen::renderAuctionHouseWindow(game::GameHandler& gameHandler) {
|
|||
// Level-Up Ding Animation
|
||||
// ============================================================
|
||||
|
||||
void GameScreen::triggerDing(uint32_t newLevel) {
|
||||
void GameScreen::triggerDing(uint32_t newLevel, uint32_t hpDelta, uint32_t manaDelta,
|
||||
uint32_t str, uint32_t agi, uint32_t sta,
|
||||
uint32_t intel, uint32_t spi) {
|
||||
dingTimer_ = DING_DURATION;
|
||||
dingLevel_ = newLevel;
|
||||
dingHpDelta_ = hpDelta;
|
||||
dingManaDelta_ = manaDelta;
|
||||
dingStats_[0] = str;
|
||||
dingStats_[1] = agi;
|
||||
dingStats_[2] = sta;
|
||||
dingStats_[3] = intel;
|
||||
dingStats_[4] = spi;
|
||||
|
||||
auto* renderer = core::Application::getInstance().getRenderer();
|
||||
if (renderer) {
|
||||
|
|
@ -18106,6 +18116,43 @@ void GameScreen::renderDingEffect() {
|
|||
// Gold text
|
||||
draw->AddText(font, fontSize, ImVec2(tx, ty),
|
||||
IM_COL32(255, 210, 0, (int)(alpha * 255)), buf);
|
||||
|
||||
// Stat gains below the main text (shown only if server sent deltas)
|
||||
bool hasStatGains = (dingHpDelta_ > 0 || dingManaDelta_ > 0 ||
|
||||
dingStats_[0] || dingStats_[1] || dingStats_[2] ||
|
||||
dingStats_[3] || dingStats_[4]);
|
||||
if (hasStatGains) {
|
||||
float smallSize = baseSize * 0.95f;
|
||||
float yOff = ty + sz.y + 6.0f;
|
||||
|
||||
// Build stat delta string: "+150 HP +80 Mana +2 Str +2 Agi ..."
|
||||
static const char* kStatLabels[] = { "Str", "Agi", "Sta", "Int", "Spi" };
|
||||
char statBuf[128];
|
||||
int written = 0;
|
||||
if (dingHpDelta_ > 0)
|
||||
written += snprintf(statBuf + written, sizeof(statBuf) - written,
|
||||
"+%u HP ", dingHpDelta_);
|
||||
if (dingManaDelta_ > 0)
|
||||
written += snprintf(statBuf + written, sizeof(statBuf) - written,
|
||||
"+%u Mana ", dingManaDelta_);
|
||||
for (int i = 0; i < 5 && written < (int)sizeof(statBuf) - 1; ++i) {
|
||||
if (dingStats_[i] > 0)
|
||||
written += snprintf(statBuf + written, sizeof(statBuf) - written,
|
||||
"+%u %s ", dingStats_[i], kStatLabels[i]);
|
||||
}
|
||||
// Trim trailing spaces
|
||||
while (written > 0 && statBuf[written - 1] == ' ') --written;
|
||||
statBuf[written] = '\0';
|
||||
|
||||
if (written > 0) {
|
||||
ImVec2 ssz = font->CalcTextSizeA(smallSize, FLT_MAX, 0.0f, statBuf);
|
||||
float stx = cx - ssz.x * 0.5f;
|
||||
draw->AddText(font, smallSize, ImVec2(stx + 1, yOff + 1),
|
||||
IM_COL32(0, 0, 0, (int)(alpha * 160)), statBuf);
|
||||
draw->AddText(font, smallSize, ImVec2(stx, yOff),
|
||||
IM_COL32(100, 220, 100, (int)(alpha * 230)), statBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameScreen::triggerAchievementToast(uint32_t achievementId, std::string name) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue