Fix loot money notifications fallback and chest loot trigger

This commit is contained in:
Kelsi 2026-02-18 04:11:00 -08:00
parent f706733e99
commit 6dc111f13f
2 changed files with 69 additions and 2 deletions

View file

@ -1377,6 +1377,10 @@ private:
bool moneyTaken = false; bool moneyTaken = false;
}; };
std::unordered_map<uint64_t, LocalLootState> localLootState_; std::unordered_map<uint64_t, LocalLootState> localLootState_;
uint64_t pendingLootMoneyGuid_ = 0;
uint32_t pendingLootMoneyAmount_ = 0;
float pendingLootMoneyNotifyTimer_ = 0.0f;
std::unordered_map<uint64_t, float> recentLootMoneyAnnounceCooldowns_;
uint64_t playerMoneyCopper_ = 0; uint64_t playerMoneyCopper_ = 0;
// Some servers/custom clients shift update field indices. We can auto-detect coinage by correlating // Some servers/custom clients shift update field indices. We can auto-detect coinage by correlating
// money-notify deltas with update-field diffs and then overriding UF::PLAYER_FIELD_COINAGE at runtime. // money-notify deltas with update-field diffs and then overriding UF::PLAYER_FIELD_COINAGE at runtime.

View file

@ -334,6 +334,48 @@ void GameHandler::update(float deltaTime) {
} }
} }
if (pendingLootMoneyNotifyTimer_ > 0.0f) {
pendingLootMoneyNotifyTimer_ -= deltaTime;
if (pendingLootMoneyNotifyTimer_ <= 0.0f) {
pendingLootMoneyNotifyTimer_ = 0.0f;
bool alreadyAnnounced = false;
if (pendingLootMoneyGuid_ != 0) {
auto it = localLootState_.find(pendingLootMoneyGuid_);
if (it != localLootState_.end()) {
alreadyAnnounced = it->second.moneyTaken;
it->second.moneyTaken = true;
}
}
if (!alreadyAnnounced && pendingLootMoneyAmount_ > 0) {
addSystemChatMessage("Looted: " + formatCopperAmount(pendingLootMoneyAmount_));
auto* renderer = core::Application::getInstance().getRenderer();
if (renderer) {
if (auto* sfx = renderer->getUiSoundManager()) {
if (pendingLootMoneyAmount_ >= 10000) {
sfx->playLootCoinLarge();
} else {
sfx->playLootCoinSmall();
}
}
}
if (pendingLootMoneyGuid_ != 0) {
recentLootMoneyAnnounceCooldowns_[pendingLootMoneyGuid_] = 1.5f;
}
}
pendingLootMoneyGuid_ = 0;
pendingLootMoneyAmount_ = 0;
}
}
for (auto it = recentLootMoneyAnnounceCooldowns_.begin(); it != recentLootMoneyAnnounceCooldowns_.end();) {
it->second -= deltaTime;
if (it->second <= 0.0f) {
it = recentLootMoneyAnnounceCooldowns_.erase(it);
} else {
++it;
}
}
// Auto-inspect throttling (fallback for player equipment visuals). // Auto-inspect throttling (fallback for player equipment visuals).
if (inspectRateLimit_ > 0.0f) { if (inspectRateLimit_ > 0.0f) {
inspectRateLimit_ = std::max(0.0f, inspectRateLimit_ - deltaTime); inspectRateLimit_ = std::max(0.0f, inspectRateLimit_ - deltaTime);
@ -1198,8 +1240,12 @@ void GameHandler::handlePacket(network::Packet& packet) {
pendingMoneyDelta_ = amount; pendingMoneyDelta_ = amount;
pendingMoneyDeltaTimer_ = 2.0f; pendingMoneyDeltaTimer_ = 2.0f;
LOG_INFO("Looted ", amount, " copper (total: ", playerMoneyCopper_, ")"); LOG_INFO("Looted ", amount, " copper (total: ", playerMoneyCopper_, ")");
uint64_t notifyGuid = pendingLootMoneyGuid_ != 0 ? pendingLootMoneyGuid_ : currentLoot.lootGuid;
pendingLootMoneyGuid_ = 0;
pendingLootMoneyAmount_ = 0;
pendingLootMoneyNotifyTimer_ = 0.0f;
bool alreadyAnnounced = false; bool alreadyAnnounced = false;
auto it = localLootState_.find(currentLoot.lootGuid); auto it = localLootState_.find(notifyGuid);
if (it != localLootState_.end()) { if (it != localLootState_.end()) {
alreadyAnnounced = it->second.moneyTaken; alreadyAnnounced = it->second.moneyTaken;
it->second.moneyTaken = true; it->second.moneyTaken = true;
@ -1216,6 +1262,9 @@ void GameHandler::handlePacket(network::Packet& packet) {
} }
} }
} }
if (notifyGuid != 0) {
recentLootMoneyAnnounceCooldowns_[notifyGuid] = 1.5f;
}
} }
} }
break; break;
@ -8029,14 +8078,17 @@ void GameHandler::interactWithGameObject(uint64_t guid) {
// For mailbox GameObjects (type 19), open mail UI and request mail list. // For mailbox GameObjects (type 19), open mail UI and request mail list.
// In Vanilla/Classic there is no SMSG_SHOW_MAILBOX — the server just sends // In Vanilla/Classic there is no SMSG_SHOW_MAILBOX — the server just sends
// animation/sound and expects the client to request the mail list. // animation/sound and expects the client to request the mail list.
bool shouldSendLoot = (entity == nullptr);
if (entity && entity->getType() == ObjectType::GAMEOBJECT) { if (entity && entity->getType() == ObjectType::GAMEOBJECT) {
auto go = std::static_pointer_cast<GameObject>(entity); auto go = std::static_pointer_cast<GameObject>(entity);
auto* info = getCachedGameObjectInfo(go->getEntry()); auto* info = getCachedGameObjectInfo(go->getEntry());
if (info && (info->type == 3 || info->type == 25)) { if (info && (info->type == 3 || info->type == 25)) {
// Lootable objects (e.g., chests/fishing holes) on some cores require // Lootable objects (e.g., chests/fishing holes) on some cores require
// explicit CMSG_LOOT in addition to CMSG_GAMEOBJECT_USE. // explicit CMSG_LOOT in addition to CMSG_GAMEOBJECT_USE.
lootTarget(guid); shouldSendLoot = true;
} }
// Keep chest/fishing loot usable when GO info cache is missing or delayed.
if (!info) shouldSendLoot = true;
if (info && info->type == 19) { if (info && info->type == 19) {
LOG_INFO("Mailbox interaction: opening mail UI and requesting mail list"); LOG_INFO("Mailbox interaction: opening mail UI and requesting mail list");
mailboxGuid_ = guid; mailboxGuid_ = guid;
@ -8047,6 +8099,9 @@ void GameHandler::interactWithGameObject(uint64_t guid) {
refreshMailList(); refreshMailList();
} }
} }
if (shouldSendLoot) {
lootTarget(guid);
}
} }
void GameHandler::selectGossipOption(uint32_t optionId) { void GameHandler::selectGossipOption(uint32_t optionId) {
@ -8529,6 +8584,14 @@ void GameHandler::handleLootResponse(network::Packet& packet) {
if (currentLoot.gold > 0) { if (currentLoot.gold > 0) {
if (state == WorldState::IN_WORLD && socket) { if (state == WorldState::IN_WORLD && socket) {
// Auto-loot gold by sending CMSG_LOOT_MONEY (server handles the rest) // Auto-loot gold by sending CMSG_LOOT_MONEY (server handles the rest)
bool suppressFallback = false;
auto cooldownIt = recentLootMoneyAnnounceCooldowns_.find(currentLoot.lootGuid);
if (cooldownIt != recentLootMoneyAnnounceCooldowns_.end() && cooldownIt->second > 0.0f) {
suppressFallback = true;
}
pendingLootMoneyGuid_ = suppressFallback ? 0 : currentLoot.lootGuid;
pendingLootMoneyAmount_ = suppressFallback ? 0 : currentLoot.gold;
pendingLootMoneyNotifyTimer_ = suppressFallback ? 0.0f : 0.4f;
auto pkt = LootMoneyPacket::build(); auto pkt = LootMoneyPacket::build();
socket->send(pkt); socket->send(pkt);
currentLoot.gold = 0; currentLoot.gold = 0;