mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 08:03:50 +00:00
Fix buff bar: opcode merge, isBuff flag, and duration countdown
Root cause: OpcodeTable::loadFromJson() cleared all mappings before loading the expansion JSON, so any WotLK opcode absent from Turtle WoW's opcodes.json (including SMSG_AURA_UPDATE and SMSG_AURA_UPDATE_ALL) was permanently lost. Changed loadFromJson to patch/merge on top of existing defaults so only explicitly listed opcodes are overridden. Also fix isBuff border color: was testing flag 0x02 (effect 2 active) instead of 0x80 (negative/debuff flag). Add client-side duration countdown: AuraSlot.receivedAtMs is stamped when the packet arrives; getRemainingMs(nowMs) subtracts elapsed time so buff tooltips show accurate remaining duration instead of stale snapshot.
This commit is contained in:
parent
df0bfaea4f
commit
55fd692c1a
4 changed files with 32 additions and 9 deletions
|
|
@ -18,8 +18,16 @@ struct AuraSlot {
|
||||||
int32_t durationMs = -1;
|
int32_t durationMs = -1;
|
||||||
int32_t maxDurationMs = -1;
|
int32_t maxDurationMs = -1;
|
||||||
uint64_t casterGuid = 0;
|
uint64_t casterGuid = 0;
|
||||||
|
uint64_t receivedAtMs = 0; // Client timestamp (ms) when durationMs was set
|
||||||
|
|
||||||
bool isEmpty() const { return spellId == 0; }
|
bool isEmpty() const { return spellId == 0; }
|
||||||
|
// Remaining duration in ms, counting down from when the packet was received
|
||||||
|
int32_t getRemainingMs(uint64_t nowMs) const {
|
||||||
|
if (durationMs < 0) return -1;
|
||||||
|
uint64_t elapsed = (nowMs > receivedAtMs) ? (nowMs - receivedAtMs) : 0;
|
||||||
|
int32_t remaining = durationMs - static_cast<int32_t>(elapsed);
|
||||||
|
return (remaining > 0) ? remaining : 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7162,7 +7162,14 @@ void GameHandler::handleAuraUpdate(network::Packet& packet, bool isAll) {
|
||||||
if (isAll) {
|
if (isAll) {
|
||||||
auraList->clear();
|
auraList->clear();
|
||||||
}
|
}
|
||||||
for (const auto& [slot, aura] : data.updates) {
|
uint64_t nowMs = static_cast<uint64_t>(
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||||
|
for (auto [slot, aura] : data.updates) {
|
||||||
|
// Stamp client timestamp so the UI can count down duration locally
|
||||||
|
if (aura.durationMs >= 0) {
|
||||||
|
aura.receivedAtMs = nowMs;
|
||||||
|
}
|
||||||
// Ensure vector is large enough
|
// Ensure vector is large enough
|
||||||
while (auraList->size() <= slot) {
|
while (auraList->size() <= slot) {
|
||||||
auraList->push_back(AuraSlot{});
|
auraList->push_back(AuraSlot{});
|
||||||
|
|
|
||||||
|
|
@ -698,13 +698,12 @@ bool OpcodeTable::loadFromJson(const std::string& path) {
|
||||||
|
|
||||||
std::string json((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
std::string json((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
// Save old tables so we can restore on failure
|
// Merge/patch on top of existing table (WotLK defaults must be loaded first).
|
||||||
|
// Opcodes NOT in the JSON keep their current mapping, so expansion-specific
|
||||||
|
// JSONs only need to list entries that differ from the WotLK baseline.
|
||||||
auto savedLogicalToWire = logicalToWire_;
|
auto savedLogicalToWire = logicalToWire_;
|
||||||
auto savedWireToLogical = wireToLogical_;
|
auto savedWireToLogical = wireToLogical_;
|
||||||
|
|
||||||
logicalToWire_.clear();
|
|
||||||
wireToLogical_.clear();
|
|
||||||
|
|
||||||
// Parse simple JSON: { "NAME": "0xHEX", ... } or { "NAME": 123, ... }
|
// Parse simple JSON: { "NAME": "0xHEX", ... } or { "NAME": 123, ... }
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
size_t loaded = 0;
|
size_t loaded = 0;
|
||||||
|
|
@ -746,6 +745,11 @@ bool OpcodeTable::loadFromJson(const std::string& path) {
|
||||||
auto logOp = nameToLogical(key);
|
auto logOp = nameToLogical(key);
|
||||||
if (logOp) {
|
if (logOp) {
|
||||||
uint16_t logIdx = static_cast<uint16_t>(*logOp);
|
uint16_t logIdx = static_cast<uint16_t>(*logOp);
|
||||||
|
// Remove stale reverse-mapping for this logical opcode's old wire value
|
||||||
|
auto oldIt = logicalToWire_.find(logIdx);
|
||||||
|
if (oldIt != logicalToWire_.end()) {
|
||||||
|
wireToLogical_.erase(oldIt->second);
|
||||||
|
}
|
||||||
logicalToWire_[logIdx] = wire;
|
logicalToWire_[logIdx] = wire;
|
||||||
wireToLogical_[wire] = logIdx;
|
wireToLogical_[wire] = logIdx;
|
||||||
++loaded;
|
++loaded;
|
||||||
|
|
|
||||||
|
|
@ -4044,7 +4044,7 @@ void GameScreen::renderBuffBar(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
ImGui::PushID(static_cast<int>(i));
|
ImGui::PushID(static_cast<int>(i));
|
||||||
|
|
||||||
bool isBuff = (aura.flags & 0x02) != 0;
|
bool isBuff = (aura.flags & 0x80) == 0; // 0x80 = negative/debuff flag
|
||||||
ImVec4 borderColor = isBuff ? ImVec4(0.2f, 0.8f, 0.2f, 0.9f) : ImVec4(0.8f, 0.2f, 0.2f, 0.9f);
|
ImVec4 borderColor = isBuff ? ImVec4(0.2f, 0.8f, 0.2f, 0.9f) : ImVec4(0.8f, 0.2f, 0.2f, 0.9f);
|
||||||
|
|
||||||
// Try to get spell icon
|
// Try to get spell icon
|
||||||
|
|
@ -4078,12 +4078,16 @@ void GameScreen::renderBuffBar(game::GameHandler& gameHandler) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tooltip with spell name and duration
|
// Tooltip with spell name and live countdown
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
std::string name = spellbookScreen.lookupSpellName(aura.spellId, assetMgr);
|
std::string name = spellbookScreen.lookupSpellName(aura.spellId, assetMgr);
|
||||||
if (name.empty()) name = "Spell #" + std::to_string(aura.spellId);
|
if (name.empty()) name = "Spell #" + std::to_string(aura.spellId);
|
||||||
if (aura.durationMs > 0) {
|
uint64_t nowMs = static_cast<uint64_t>(
|
||||||
int seconds = aura.durationMs / 1000;
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||||
|
int32_t remaining = aura.getRemainingMs(nowMs);
|
||||||
|
if (remaining > 0) {
|
||||||
|
int seconds = remaining / 1000;
|
||||||
if (seconds < 60) {
|
if (seconds < 60) {
|
||||||
ImGui::SetTooltip("%s (%ds)", name.c_str(), seconds);
|
ImGui::SetTooltip("%s (%ds)", name.c_str(), seconds);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue