mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Implement group loot roll: SMSG_LOOT_ROLL, SMSG_LOOT_ROLL_WON, CMSG_LOOT_ROLL
- Parse SMSG_LOOT_ROLL: if rollType==128 and it's our player, store pending roll (itemId, slot, name from itemInfoCache_) and show popup; otherwise show chat notification of another player's roll result - Parse SMSG_LOOT_ROLL_WON: show winner announcement in chat with item name and roll type/value - sendLootRoll() sends CMSG_LOOT_ROLL (objectGuid+slot+rollType) and clears pending roll state - SMSG_LOOT_MASTER_LIST consumed silently (no UI yet) - renderLootRollPopup(): ImGui window with Need/Greed/Disenchant/Pass buttons; item name colored by quality (poor/common/uncommon/rare/epic/ legendary color scale)
This commit is contained in:
parent
2d124e7e54
commit
3114e80fa8
4 changed files with 194 additions and 0 deletions
|
|
@ -758,6 +758,19 @@ public:
|
|||
void setAutoLoot(bool enabled) { autoLoot_ = enabled; }
|
||||
bool isAutoLoot() const { return autoLoot_; }
|
||||
|
||||
// Group loot roll
|
||||
struct LootRollEntry {
|
||||
uint64_t objectGuid = 0;
|
||||
uint32_t slot = 0;
|
||||
uint32_t itemId = 0;
|
||||
std::string itemName;
|
||||
uint8_t itemQuality = 0;
|
||||
};
|
||||
bool hasPendingLootRoll() const { return pendingLootRollActive_; }
|
||||
const LootRollEntry& getPendingLootRoll() const { return pendingLootRoll_; }
|
||||
void sendLootRoll(uint64_t objectGuid, uint32_t slot, uint8_t rollType);
|
||||
// rollType: 0=need, 1=greed, 2=disenchant, 96=pass
|
||||
|
||||
// NPC Gossip
|
||||
void interactWithNpc(uint64_t guid);
|
||||
void interactWithGameObject(uint64_t guid);
|
||||
|
|
@ -1258,6 +1271,8 @@ private:
|
|||
void handleDuelRequested(network::Packet& packet);
|
||||
void handleDuelComplete(network::Packet& packet);
|
||||
void handleDuelWinner(network::Packet& packet);
|
||||
void handleLootRoll(network::Packet& packet);
|
||||
void handleLootRollWon(network::Packet& packet);
|
||||
|
||||
// ---- LFG / Dungeon Finder handlers ----
|
||||
void handleLfgJoinResult(network::Packet& packet);
|
||||
|
|
@ -1637,6 +1652,10 @@ private:
|
|||
bool lootWindowOpen = false;
|
||||
bool autoLoot_ = false;
|
||||
LootResponseData currentLoot;
|
||||
|
||||
// Group loot roll state
|
||||
bool pendingLootRollActive_ = false;
|
||||
LootRollEntry pendingLootRoll_;
|
||||
struct LocalLootState {
|
||||
LootResponseData data;
|
||||
bool moneyTaken = false;
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ private:
|
|||
void renderPartyFrames(game::GameHandler& gameHandler);
|
||||
void renderGroupInvitePopup(game::GameHandler& gameHandler);
|
||||
void renderDuelRequestPopup(game::GameHandler& gameHandler);
|
||||
void renderLootRollPopup(game::GameHandler& gameHandler);
|
||||
void renderBuffBar(game::GameHandler& gameHandler);
|
||||
void renderLootWindow(game::GameHandler& gameHandler);
|
||||
void renderGossipWindow(game::GameHandler& gameHandler);
|
||||
|
|
|
|||
|
|
@ -1953,6 +1953,16 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
case Opcode::SMSG_LOOT_REMOVED:
|
||||
handleLootRemoved(packet);
|
||||
break;
|
||||
case Opcode::SMSG_LOOT_ROLL:
|
||||
handleLootRoll(packet);
|
||||
break;
|
||||
case Opcode::SMSG_LOOT_ROLL_WON:
|
||||
handleLootRollWon(packet);
|
||||
break;
|
||||
case Opcode::SMSG_LOOT_MASTER_LIST:
|
||||
// Master looter list — no UI yet; consume to avoid unhandled warning.
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
case Opcode::SMSG_GOSSIP_MESSAGE:
|
||||
handleGossipMessage(packet);
|
||||
break;
|
||||
|
|
@ -14948,6 +14958,122 @@ void GameHandler::handleAuctionCommandResult(network::Packet& packet) {
|
|||
" error=", result.errorCode);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Group loot roll (SMSG_LOOT_ROLL / SMSG_LOOT_ROLL_WON / CMSG_LOOT_ROLL)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void GameHandler::handleLootRoll(network::Packet& packet) {
|
||||
// uint64 objectGuid, uint32 slot, uint64 playerGuid,
|
||||
// uint32 itemId, uint32 randomSuffix, uint32 randomPropId,
|
||||
// uint8 rollNumber, uint8 rollType
|
||||
size_t rem = packet.getSize() - packet.getReadPos();
|
||||
if (rem < 26) return; // minimum: 8+4+8+4+4+4+1+1 = 34, be lenient
|
||||
|
||||
uint64_t objectGuid = packet.readUInt64();
|
||||
uint32_t slot = packet.readUInt32();
|
||||
uint64_t rollerGuid = packet.readUInt64();
|
||||
uint32_t itemId = packet.readUInt32();
|
||||
/*uint32_t randSuffix =*/ packet.readUInt32();
|
||||
/*uint32_t randProp =*/ packet.readUInt32();
|
||||
uint8_t rollNum = packet.readUInt8();
|
||||
uint8_t rollType = packet.readUInt8();
|
||||
|
||||
// rollType 128 = "waiting for this player to roll"
|
||||
if (rollType == 128 && rollerGuid == playerGuid) {
|
||||
// Server is asking us to roll; present the roll UI.
|
||||
pendingLootRollActive_ = true;
|
||||
pendingLootRoll_.objectGuid = objectGuid;
|
||||
pendingLootRoll_.slot = slot;
|
||||
pendingLootRoll_.itemId = itemId;
|
||||
// Look up item name from cache
|
||||
auto* info = getItemInfo(itemId);
|
||||
pendingLootRoll_.itemName = info ? info->name : std::to_string(itemId);
|
||||
pendingLootRoll_.itemQuality = info ? static_cast<uint8_t>(info->quality) : 0;
|
||||
LOG_INFO("SMSG_LOOT_ROLL: need/greed prompt for item=", itemId,
|
||||
" (", pendingLootRoll_.itemName, ") slot=", slot);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise it's reporting another player's roll result
|
||||
const char* rollNames[] = {"Need", "Greed", "Disenchant", "Pass"};
|
||||
const char* rollName = (rollType < 4) ? rollNames[rollType] : "Pass";
|
||||
|
||||
std::string rollerName;
|
||||
auto entity = entityManager.getEntity(rollerGuid);
|
||||
if (auto* unit = dynamic_cast<Unit*>(entity.get())) {
|
||||
rollerName = unit->getName();
|
||||
}
|
||||
if (rollerName.empty()) rollerName = "Someone";
|
||||
|
||||
auto* info = getItemInfo(itemId);
|
||||
std::string iName = info ? info->name : std::to_string(itemId);
|
||||
|
||||
char buf[256];
|
||||
std::snprintf(buf, sizeof(buf), "%s rolls %s (%d) on [%s]",
|
||||
rollerName.c_str(), rollName, static_cast<int>(rollNum), iName.c_str());
|
||||
addSystemChatMessage(buf);
|
||||
|
||||
LOG_DEBUG("SMSG_LOOT_ROLL: ", rollerName, " rolled ", rollName,
|
||||
" (", rollNum, ") on item ", itemId);
|
||||
(void)objectGuid; (void)slot;
|
||||
}
|
||||
|
||||
void GameHandler::handleLootRollWon(network::Packet& packet) {
|
||||
size_t rem = packet.getSize() - packet.getReadPos();
|
||||
if (rem < 26) return;
|
||||
|
||||
/*uint64_t objectGuid =*/ packet.readUInt64();
|
||||
/*uint32_t slot =*/ packet.readUInt32();
|
||||
uint64_t winnerGuid = packet.readUInt64();
|
||||
uint32_t itemId = packet.readUInt32();
|
||||
/*uint32_t randSuffix =*/ packet.readUInt32();
|
||||
/*uint32_t randProp =*/ packet.readUInt32();
|
||||
uint8_t rollNum = packet.readUInt8();
|
||||
uint8_t rollType = packet.readUInt8();
|
||||
|
||||
const char* rollNames[] = {"Need", "Greed", "Disenchant"};
|
||||
const char* rollName = (rollType < 3) ? rollNames[rollType] : "Roll";
|
||||
|
||||
std::string winnerName;
|
||||
auto entity = entityManager.getEntity(winnerGuid);
|
||||
if (auto* unit = dynamic_cast<Unit*>(entity.get())) {
|
||||
winnerName = unit->getName();
|
||||
}
|
||||
if (winnerName.empty()) {
|
||||
winnerName = (winnerGuid == playerGuid) ? "You" : "Someone";
|
||||
}
|
||||
|
||||
auto* info = getItemInfo(itemId);
|
||||
std::string iName = info ? info->name : std::to_string(itemId);
|
||||
|
||||
char buf[256];
|
||||
std::snprintf(buf, sizeof(buf), "%s wins [%s] (%s %d)!",
|
||||
winnerName.c_str(), iName.c_str(), rollName, static_cast<int>(rollNum));
|
||||
addSystemChatMessage(buf);
|
||||
|
||||
// Clear pending roll if it was ours
|
||||
if (pendingLootRollActive_ && winnerGuid == playerGuid) {
|
||||
pendingLootRollActive_ = false;
|
||||
}
|
||||
LOG_INFO("SMSG_LOOT_ROLL_WON: winner=", winnerName, " item=", itemId,
|
||||
" roll=", rollName, "(", rollNum, ")");
|
||||
}
|
||||
|
||||
void GameHandler::sendLootRoll(uint64_t objectGuid, uint32_t slot, uint8_t rollType) {
|
||||
if (state != WorldState::IN_WORLD || !socket) return;
|
||||
pendingLootRollActive_ = false;
|
||||
|
||||
network::Packet pkt(wireOpcode(Opcode::CMSG_LOOT_ROLL));
|
||||
pkt.writeUInt64(objectGuid);
|
||||
pkt.writeUInt32(slot);
|
||||
pkt.writeUInt8(rollType);
|
||||
socket->send(pkt);
|
||||
|
||||
const char* rollNames[] = {"Need", "Greed", "Disenchant", "Pass"};
|
||||
const char* rName = (rollType < 3) ? rollNames[rollType] : "Pass";
|
||||
LOG_INFO("CMSG_LOOT_ROLL: type=", rName, " item=", pendingLootRoll_.itemName);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SMSG_ACHIEVEMENT_EARNED (WotLK 3.3.5a wire 0x4AB)
|
||||
// uint64 guid — player who earned it (may be another player)
|
||||
|
|
|
|||
|
|
@ -397,6 +397,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderPartyFrames(gameHandler);
|
||||
renderGroupInvitePopup(gameHandler);
|
||||
renderDuelRequestPopup(gameHandler);
|
||||
renderLootRollPopup(gameHandler);
|
||||
renderGuildInvitePopup(gameHandler);
|
||||
renderGuildRoster(gameHandler);
|
||||
renderBuffBar(gameHandler);
|
||||
|
|
@ -4401,6 +4402,53 @@ void GameScreen::renderDuelRequestPopup(game::GameHandler& gameHandler) {
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
void GameScreen::renderLootRollPopup(game::GameHandler& gameHandler) {
|
||||
if (!gameHandler.hasPendingLootRoll()) return;
|
||||
|
||||
const auto& roll = gameHandler.getPendingLootRoll();
|
||||
|
||||
auto* window = core::Application::getInstance().getWindow();
|
||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 310), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(350, 0), ImGuiCond_Always);
|
||||
|
||||
if (ImGui::Begin("Loot Roll", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize)) {
|
||||
// Quality color for item name
|
||||
static const ImVec4 kQualityColors[] = {
|
||||
ImVec4(0.6f, 0.6f, 0.6f, 1.0f), // 0=poor (grey)
|
||||
ImVec4(1.0f, 1.0f, 1.0f, 1.0f), // 1=common (white)
|
||||
ImVec4(0.1f, 1.0f, 0.1f, 1.0f), // 2=uncommon (green)
|
||||
ImVec4(0.0f, 0.44f, 0.87f, 1.0f),// 3=rare (blue)
|
||||
ImVec4(0.64f, 0.21f, 0.93f, 1.0f),// 4=epic (purple)
|
||||
ImVec4(1.0f, 0.5f, 0.0f, 1.0f), // 5=legendary (orange)
|
||||
};
|
||||
uint8_t q = roll.itemQuality;
|
||||
ImVec4 col = (q < 6) ? kQualityColors[q] : kQualityColors[1];
|
||||
|
||||
ImGui::Text("An item is up for rolls:");
|
||||
ImGui::TextColored(col, "[%s]", roll.itemName.c_str());
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button("Need", ImVec2(80, 30))) {
|
||||
gameHandler.sendLootRoll(roll.objectGuid, roll.slot, 0);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Greed", ImVec2(80, 30))) {
|
||||
gameHandler.sendLootRoll(roll.objectGuid, roll.slot, 1);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Disenchant", ImVec2(95, 30))) {
|
||||
gameHandler.sendLootRoll(roll.objectGuid, roll.slot, 2);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Pass", ImVec2(70, 30))) {
|
||||
gameHandler.sendLootRoll(roll.objectGuid, roll.slot, 96);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void GameScreen::renderGuildInvitePopup(game::GameHandler& gameHandler) {
|
||||
if (!gameHandler.hasPendingGuildInvite()) return;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue