mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Add ready check popup UI and fix party leader lookup
- Implement renderReadyCheckPopup() showing initiator name with Ready/Not Ready buttons - Fix MSG_RAID_READY_CHECK fallback: use partyData.leaderGuid instead of non-existent isLeader field - Add faction standing handler with loadFactionNameCache() (Faction.dbc field 22) - Add gossip POI handler writing canonical WoW coords to gossipPois_ for minimap rendering
This commit is contained in:
parent
f89840a6aa
commit
26eefe9529
4 changed files with 158 additions and 4 deletions
|
|
@ -394,6 +394,9 @@ public:
|
|||
// Ready check
|
||||
void initiateReadyCheck();
|
||||
void respondToReadyCheck(bool ready);
|
||||
bool hasPendingReadyCheck() const { return pendingReadyCheck_; }
|
||||
void dismissReadyCheck() { pendingReadyCheck_ = false; }
|
||||
const std::string& getReadyCheckInitiator() const { return readyCheckInitiator_; }
|
||||
|
||||
// Duel
|
||||
void forfeitDuel();
|
||||
|
|
@ -898,6 +901,7 @@ public:
|
|||
int32_t standing = 0;
|
||||
};
|
||||
const std::vector<FactionStandingInit>& getInitialFactions() const { return initialFactions_; }
|
||||
const std::unordered_map<uint32_t, int32_t>& getFactionStandings() const { return factionStandings_; }
|
||||
uint32_t getLastContactListMask() const { return lastContactListMask_; }
|
||||
uint32_t getLastContactListCount() const { return lastContactListCount_; }
|
||||
bool isServerMovementAllowed() const { return serverMovementAllowed_; }
|
||||
|
|
@ -1700,6 +1704,18 @@ private:
|
|||
int32_t lfgAvgWaitSec_ = -1; // estimated wait, -1=unknown
|
||||
uint32_t lfgTimeInQueueMs_= 0; // ms already in queue
|
||||
|
||||
// Ready check state
|
||||
bool pendingReadyCheck_ = false;
|
||||
std::string readyCheckInitiator_;
|
||||
|
||||
// Faction standings (factionId → absolute standing value)
|
||||
std::unordered_map<uint32_t, int32_t> factionStandings_;
|
||||
// Faction name cache (factionId → name), populated lazily from Faction.dbc
|
||||
std::unordered_map<uint32_t, std::string> factionNameCache_;
|
||||
bool factionNameCacheLoaded_ = false;
|
||||
void loadFactionNameCache();
|
||||
std::string getFactionName(uint32_t factionId) const;
|
||||
|
||||
// ---- Phase 4: Group ----
|
||||
GroupListData partyData;
|
||||
bool pendingGroupInvite = false;
|
||||
|
|
|
|||
|
|
@ -228,6 +228,7 @@ private:
|
|||
void renderMinimapMarkers(game::GameHandler& gameHandler);
|
||||
void renderGuildRoster(game::GameHandler& gameHandler);
|
||||
void renderGuildInvitePopup(game::GameHandler& gameHandler);
|
||||
void renderReadyCheckPopup(game::GameHandler& gameHandler);
|
||||
void renderChatBubbles(game::GameHandler& gameHandler);
|
||||
void renderMailWindow(game::GameHandler& gameHandler);
|
||||
void renderMailComposeWindow(game::GameHandler& gameHandler);
|
||||
|
|
|
|||
|
|
@ -2399,12 +2399,32 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
case Opcode::SMSG_PARTY_MEMBER_STATS_FULL:
|
||||
handlePartyMemberStats(packet, true);
|
||||
break;
|
||||
case Opcode::MSG_RAID_READY_CHECK:
|
||||
// Server ready-check prompt (minimal handling for now).
|
||||
packet.setReadPos(packet.getSize());
|
||||
case Opcode::MSG_RAID_READY_CHECK: {
|
||||
// Server is broadcasting a ready check (someone in the raid initiated it).
|
||||
// Payload: empty body, or optional uint64 initiator GUID in some builds.
|
||||
pendingReadyCheck_ = true;
|
||||
readyCheckInitiator_.clear();
|
||||
if (packet.getSize() - packet.getReadPos() >= 8) {
|
||||
uint64_t initiatorGuid = packet.readUInt64();
|
||||
auto entity = entityManager.getEntity(initiatorGuid);
|
||||
if (auto* unit = dynamic_cast<Unit*>(entity.get())) {
|
||||
readyCheckInitiator_ = unit->getName();
|
||||
}
|
||||
}
|
||||
if (readyCheckInitiator_.empty() && partyData.leaderGuid != 0) {
|
||||
// Identify initiator from party leader
|
||||
for (const auto& member : partyData.members) {
|
||||
if (member.guid == partyData.leaderGuid) { readyCheckInitiator_ = member.name; break; }
|
||||
}
|
||||
}
|
||||
addSystemChatMessage(readyCheckInitiator_.empty()
|
||||
? "Ready check initiated!"
|
||||
: readyCheckInitiator_ + " initiated a ready check!");
|
||||
LOG_INFO("MSG_RAID_READY_CHECK: initiator=", readyCheckInitiator_);
|
||||
break;
|
||||
}
|
||||
case Opcode::MSG_RAID_READY_CHECK_CONFIRM:
|
||||
// Ready-check responses from members.
|
||||
// Another member responded to the ready check — consume.
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
case Opcode::SMSG_RAID_INSTANCE_INFO:
|
||||
|
|
@ -2686,6 +2706,40 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case Opcode::SMSG_SET_FACTION_STANDING: {
|
||||
// uint8 showVisualEffect + uint32 count + count × (uint32 factionId + int32 standing)
|
||||
if (packet.getSize() - packet.getReadPos() < 5) break;
|
||||
/*uint8_t showVisual =*/ packet.readUInt8();
|
||||
uint32_t count = packet.readUInt32();
|
||||
count = std::min(count, 128u);
|
||||
loadFactionNameCache();
|
||||
for (uint32_t i = 0; i < count && packet.getSize() - packet.getReadPos() >= 8; ++i) {
|
||||
uint32_t factionId = packet.readUInt32();
|
||||
int32_t standing = static_cast<int32_t>(packet.readUInt32());
|
||||
int32_t oldStanding = 0;
|
||||
auto it = factionStandings_.find(factionId);
|
||||
if (it != factionStandings_.end()) oldStanding = it->second;
|
||||
factionStandings_[factionId] = standing;
|
||||
int32_t delta = standing - oldStanding;
|
||||
if (delta != 0) {
|
||||
std::string name = getFactionName(factionId);
|
||||
char buf[256];
|
||||
std::snprintf(buf, sizeof(buf), "Reputation with %s %s by %d.",
|
||||
name.c_str(),
|
||||
delta > 0 ? "increased" : "decreased",
|
||||
std::abs(delta));
|
||||
addSystemChatMessage(buf);
|
||||
}
|
||||
LOG_DEBUG("SMSG_SET_FACTION_STANDING: faction=", factionId, " standing=", standing);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Opcode::SMSG_SET_FACTION_ATWAR:
|
||||
case Opcode::SMSG_SET_FACTION_VISIBLE:
|
||||
// uint32 factionId [+ uint8 flags for ATWAR] — consume; hostility is tracked via update fields
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
|
||||
case Opcode::SMSG_FEATURE_SYSTEM_STATUS:
|
||||
case Opcode::SMSG_SET_FLAT_SPELL_MODIFIER:
|
||||
case Opcode::SMSG_SET_PCT_SPELL_MODIFIER:
|
||||
|
|
@ -15912,5 +15966,55 @@ void GameHandler::handleAchievementEarned(network::Packet& packet) {
|
|||
" achievementId=", achievementId, " self=", isSelf);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Faction name cache (lazily loaded from Faction.dbc)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void GameHandler::loadFactionNameCache() {
|
||||
if (factionNameCacheLoaded_) return;
|
||||
factionNameCacheLoaded_ = true;
|
||||
|
||||
auto* am = core::Application::getInstance().getAssetManager();
|
||||
if (!am || !am->isInitialized()) return;
|
||||
|
||||
auto dbc = am->loadDBC("Faction.dbc");
|
||||
if (!dbc || !dbc->isLoaded()) return;
|
||||
|
||||
// Faction.dbc WotLK 3.3.5a field layout:
|
||||
// 0: ID
|
||||
// 1-4: ReputationRaceMask[4]
|
||||
// 5-8: ReputationClassMask[4]
|
||||
// 9-12: ReputationBase[4]
|
||||
// 13-16: ReputationFlags[4]
|
||||
// 17: ParentFactionID
|
||||
// 18-19: Spillover rates (floats)
|
||||
// 20-21: MaxRank
|
||||
// 22: Name (English locale, string ref)
|
||||
constexpr uint32_t ID_FIELD = 0;
|
||||
constexpr uint32_t NAME_FIELD = 22; // enUS name string
|
||||
|
||||
if (dbc->getFieldCount() <= NAME_FIELD) {
|
||||
LOG_WARNING("Faction.dbc: unexpected field count ", dbc->getFieldCount());
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t count = dbc->getRecordCount();
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
uint32_t factionId = dbc->getUInt32(i, ID_FIELD);
|
||||
if (factionId == 0) continue;
|
||||
std::string name = dbc->getString(i, NAME_FIELD);
|
||||
if (!name.empty()) {
|
||||
factionNameCache_[factionId] = std::move(name);
|
||||
}
|
||||
}
|
||||
LOG_INFO("Faction.dbc: loaded ", factionNameCache_.size(), " faction names");
|
||||
}
|
||||
|
||||
std::string GameHandler::getFactionName(uint32_t factionId) const {
|
||||
auto it = factionNameCache_.find(factionId);
|
||||
if (it != factionNameCache_.end()) return it->second;
|
||||
return "faction #" + std::to_string(factionId);
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
|
|||
|
|
@ -404,6 +404,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderSharedQuestPopup(gameHandler);
|
||||
renderItemTextWindow(gameHandler);
|
||||
renderGuildInvitePopup(gameHandler);
|
||||
renderReadyCheckPopup(gameHandler);
|
||||
renderGuildRoster(gameHandler);
|
||||
renderBuffBar(gameHandler);
|
||||
renderLootWindow(gameHandler);
|
||||
|
|
@ -4650,6 +4651,38 @@ void GameScreen::renderGuildInvitePopup(game::GameHandler& gameHandler) {
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
void GameScreen::renderReadyCheckPopup(game::GameHandler& gameHandler) {
|
||||
if (!gameHandler.hasPendingReadyCheck()) return;
|
||||
|
||||
auto* window = core::Application::getInstance().getWindow();
|
||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, screenH / 2 - 60), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(350, 0), ImGuiCond_Always);
|
||||
|
||||
if (ImGui::Begin("Ready Check", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize)) {
|
||||
const std::string& initiator = gameHandler.getReadyCheckInitiator();
|
||||
if (initiator.empty()) {
|
||||
ImGui::Text("A ready check has been initiated!");
|
||||
} else {
|
||||
ImGui::TextWrapped("%s has initiated a ready check!", initiator.c_str());
|
||||
}
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button("Ready", ImVec2(155, 30))) {
|
||||
gameHandler.respondToReadyCheck(true);
|
||||
gameHandler.dismissReadyCheck();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Not Ready", ImVec2(155, 30))) {
|
||||
gameHandler.respondToReadyCheck(false);
|
||||
gameHandler.dismissReadyCheck();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void GameScreen::renderGuildRoster(game::GameHandler& gameHandler) {
|
||||
// O key toggle (WoW default Social/Guild keybind)
|
||||
if (!ImGui::GetIO().WantCaptureKeyboard && ImGui::IsKeyPressed(ImGuiKey_O)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue