/* * Copyright (C) 2008-2010 Trinity * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Common.h" #include "SharedDefines.h" #include "Group.h" #include "Player.h" #include "SocialMgr.h" #include "LFGMgr.h" #include "ObjectMgr.h" #include "DisableMgr.h" #include "WorldPacket.h" LFGMgr::LFGMgr() { m_QueueTimer = 0; m_avgWaitTime = -1; m_waitTimeTanks = -1; m_waitTimeHealer = -1; m_waitTimeDps = -1; m_update = true; m_lfgProposalId = 1; } LFGMgr::~LFGMgr() { // FIXME: RewardList to be removed -> query quest system for (LfgRewardList::iterator it = m_RewardList.begin(); it != m_RewardList.end(); ++it) delete *it; m_RewardList.clear(); // FIXME: RewardDoneList to be removed -> query quest system for (LfgRewardList::iterator it = m_RewardDoneList.begin(); it != m_RewardDoneList.end(); ++it) delete *it; m_RewardDoneList.clear(); for (LfgQueueInfoMap::iterator it = m_QueueInfoMap.begin(); it != m_QueueInfoMap.end(); ++it) delete it->second; m_QueueInfoMap.clear(); for (LfgProposalMap::iterator it = m_Proposals.begin(); it != m_Proposals.end(); ++it) delete it->second; m_Proposals.clear(); for (LfgRoleCheckMap::iterator it = m_RoleChecks.begin(); it != m_RoleChecks.end(); ++it) delete it->second; m_RoleChecks.clear(); for (LfgDungeonMap::iterator it = m_CachedDungeonMap.begin(); it != m_CachedDungeonMap.end(); ++it) delete it->second; m_CachedDungeonMap.clear(); m_QueueInfoMap.clear(); m_currentQueue.clear(); m_newToQueue.clear(); } void LFGMgr::Update(uint32 diff) { if (!m_update) return; m_update = false; time_t currTime = time(NULL); // Remove obsolete role checks LfgRoleCheckMap::iterator itRoleCheck; LfgRoleCheck *pRoleCheck; for (LfgRoleCheckMap::iterator it = m_RoleChecks.begin(); it != m_RoleChecks.end();) { itRoleCheck = it++; pRoleCheck = itRoleCheck->second; if (currTime < pRoleCheck->cancelTime) continue; pRoleCheck->result = LFG_ROLECHECK_MISSING_ROLE; WorldPacket data(SMSG_LFG_ROLE_CHECK_UPDATE, 4 + 1 + 1 + pRoleCheck->dungeons.size() * 4 + 1 + pRoleCheck->roles.size() * (8 + 1 + 4 + 1)); sLog.outDebug("SMSG_LFG_ROLE_CHECK_UPDATE"); BuildLfgRoleCheck(data, pRoleCheck); Player *plr = NULL; for (LfgRolesMap::const_iterator itRoles = pRoleCheck->roles.begin(); itRoles != pRoleCheck->roles.end(); ++itRoles) { plr = sObjectMgr.GetPlayer(itRoles->first); if (!plr) continue; plr->GetSession()->SendPacket(&data); plr->GetLfgDungeons()->clear(); plr->SetLfgRoles(ROLE_NONE); if (itRoles->first == pRoleCheck->leader) plr->GetSession()->SendLfgJoinResult(LFG_JOIN_FAILED, pRoleCheck->result); } delete pRoleCheck; m_RoleChecks.erase(itRoleCheck); } // Remove obsolete proposals LfgProposalMap::iterator itRemove; for (LfgProposalMap::iterator it = m_Proposals.begin(); it != m_Proposals.end();) { itRemove = it++; if (itRemove->second->cancelTime < currTime) RemoveProposal(itRemove, LFG_UPDATETYPE_PROPOSAL_FAILED); } // Check if a proposal can be formed with the new groups being added LfgProposalList proposals; LfgGuidList firstNew; while(!m_newToQueue.empty()) { firstNew.push_back(m_newToQueue.front()); FindNewGroups(firstNew, m_currentQueue, &proposals); if (proposals.size()) // Group found! { LfgProposal *pProposal = *proposals.begin(); // TODO: Create algorithm to select better group based on GS (uses to be good tank with bad healer and viceversa) // Remove groups in the proposal from the queue for (LfgGuidList::const_iterator it = pProposal->queues.begin(); it != pProposal->queues.end(); ++it) m_currentQueue.remove(*it); m_Proposals[++m_lfgProposalId] = pProposal; for (LfgProposalPlayerMap::const_iterator itPlayers = pProposal->players.begin(); itPlayers != pProposal->players.end(); ++itPlayers) { if (Player *plr = sObjectMgr.GetPlayer(itPlayers->first)) { if (plr->GetGroup()) plr->GetSession()->SendLfgUpdateParty(LFG_UPDATETYPE_PROPOSAL_BEGIN); else plr->GetSession()->SendLfgUpdatePlayer(LFG_UPDATETYPE_PROPOSAL_BEGIN); SendUpdateProposal(plr, m_lfgProposalId, pProposal); } } // Clean up for (LfgProposalList::iterator it = proposals.begin(); it != proposals.end(); ++it) { if ((*it) == pProposal) // Do not remove the selected proposal; continue; (*it)->queues.clear(); for (LfgProposalPlayerMap::iterator itPlayers = (*it)->players.begin(); itPlayers != (*it)->players.end(); ++itPlayers) delete itPlayers->second; (*it)->players.clear(); delete (*it); } proposals.clear(); } else m_currentQueue.push_back(m_newToQueue.front()); // Group not found, add this group to the queue. m_newToQueue.pop_front(); firstNew.clear(); } // Update all players status queue info if (m_QueueTimer > LFG_QUEUEUPDATE_INTERVAL) { m_QueueTimer = 0; time_t currTime = time(NULL); int32 waitTime; LfgQueueInfo *queue; uint32 dungeonId; uint32 queuedTime; uint8 role; for (LfgQueueInfoMap::const_iterator itQueue = m_QueueInfoMap.begin(); itQueue != m_QueueInfoMap.end(); ++itQueue) { queue = itQueue->second; if (!queue) { if (IS_GROUP(itQueue->first)) sLog.outError("LFGMgr::Update: group (lowguid: %u) queued with null queue info!", GUID_LOPART(itQueue->first)); else sLog.outError("LFGMgr::Update: player (lowguid: %u) queued with null queue info!", GUID_LOPART(itQueue->first)); continue; } dungeonId = *queue->dungeons.begin(); queuedTime = uint32(currTime - queue->joinTime); role = ROLE_NONE; for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) role |= itPlayer->second; waitTime = -1; if (role & ROLE_TANK) { if (role & ROLE_HEALER || role & ROLE_DAMAGE) waitTime = m_avgWaitTime; else waitTime = m_waitTimeTanks; } else if (role & ROLE_HEALER) { if (role & ROLE_DAMAGE) waitTime = m_avgWaitTime; else waitTime = m_waitTimeHealer; } else if (role & ROLE_DAMAGE) waitTime = m_waitTimeDps; for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) if (Player * plr = sObjectMgr.GetPlayer(itPlayer->first)) plr->GetSession()->SendLfgQueueStatus(dungeonId, waitTime, m_avgWaitTime, m_waitTimeTanks, m_waitTimeHealer, m_waitTimeDps, queuedTime, queue->tanks, queue->healers, queue->dps); } } else m_QueueTimer += diff; m_update = true; } /// /// Initialize Looking For Group /// void LFGMgr::InitLFG() { // Fill reward data (to be removed -> query quest system) LfgReward *reward; for (uint8 i = 0; i <= LFG_REWARD_DATA_SIZE; ++i) { reward = new LfgReward(); reward->strangers = 0; reward->baseXP = RewardDungeonData[i][0]; reward->baseMoney = RewardDungeonData[i][1]; reward->variableMoney = 0; reward->variableXP = 0; reward->itemId = RewardDungeonData[i][2]; reward->displayId = RewardDungeonData[i][3]; reward->stackCount = RewardDungeonData[i][4]; m_RewardList.push_back(reward); } for (uint8 i = 0; i < LFG_REWARD_DATA_SIZE; ++i) { reward = new LfgReward(); reward->strangers = 0; reward->baseXP = RewardDungeonDoneData[i][0]; reward->baseMoney = RewardDungeonDoneData[i][1]; reward->variableMoney = 0; reward->variableXP = 0; reward->itemId = RewardDungeonDoneData[i][2]; reward->displayId = RewardDungeonDoneData[i][3]; reward->stackCount = RewardDungeonDoneData[i][4]; m_RewardDoneList.push_back(reward); } // Initialize dungeonMap GetAllDungeons(); } /// /// Adds the player/group to lfg queue /// /// Player void LFGMgr::Join(Player *plr) { Group *grp = plr->GetGroup(); if (grp && grp->GetLeaderGUID() != plr->GetGUID()) return; uint64 guid = grp ? grp->GetGUID() : plr->GetGUID(); LfgJoinResult result = LFG_JOIN_OK; // Previous checks before joining if (m_QueueInfoMap[guid]) { result = LFG_JOIN_INTERNAL_ERROR; if (grp) sLog.outError("LFGMgr::Join: group (lowguid: %u) trying to join but is already in queue!", grp->GetLowGUID()); else sLog.outError("LFGMgr::Join: Player (lowguid: %u) trying to join but is already in queue!", plr->GetGUIDLow()); } else if (plr->InBattleground() || plr->InArena()) result = LFG_JOIN_USING_BG_SYSTEM; else if (plr->HasAura(LFG_SPELL_DESERTER)) result = LFG_JOIN_DESERTER; else if (plr->HasAura(LFG_SPELL_COOLDOWN)) result = LFG_JOIN_RANDOM_COOLDOWN; else { // Check if all dungeons are valid for (LfgDungeonSet::const_iterator it = plr->GetLfgDungeons()->begin(); it != plr->GetLfgDungeons()->end(); ++it) { if (!GetDungeonGroupType(*it)) { result = LFG_JOIN_DUNGEON_INVALID; break; } } } // Group checks if (grp && result == LFG_JOIN_OK) { if (grp->GetMembersCount() > MAXGROUPSIZE) result = LFG_JOIN_TOO_MUCH_MEMBERS; else { Player *plrg; uint8 memberCount = 0; for (GroupReference *itr = grp->GetFirstMember(); itr != NULL && result == LFG_JOIN_OK; itr = itr->next()) { plrg = itr->getSource(); if (plrg) { if (plrg->HasAura(LFG_SPELL_DESERTER)) result = LFG_JOIN_PARTY_DESERTER; else if (plrg->HasAura(LFG_SPELL_COOLDOWN)) result = LFG_JOIN_PARTY_RANDOM_COOLDOWN; ++memberCount; } } if (memberCount != grp->GetMembersCount()) result = LFG_JOIN_DISCONNECTED; } } if (result != LFG_JOIN_OK) // Someone can't join. Clear all stuf { plr->GetLfgDungeons()->clear(); plr->SetLfgRoles(ROLE_NONE); plr->GetSession()->SendLfgJoinResult(result, 0); plr->GetSession()->SendLfgUpdateParty(LFG_UPDATETYPE_ROLECHECK_FAILED); return; } if (grp) { Player *plrg = NULL; LfgDungeonSet *dungeons; for (GroupReference *itr = plr->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { plrg = itr->getSource(); // Not null, checked earlier dungeons = plrg->GetLfgDungeons(); for (LfgDungeonSet::const_iterator itDungeon = plr->GetLfgDungeons()->begin(); itDungeon != plr->GetLfgDungeons()->end(); ++itDungeon) dungeons->insert(*itDungeon); plrg->GetSession()->SendLfgUpdateParty(LFG_UPDATETYPE_JOIN_PROPOSAL); } UpdateRoleCheck(grp, plr); } else { plr->GetSession()->SendLfgJoinResult(LFG_JOIN_OK, 0); plr->GetSession()->SendLfgUpdatePlayer(LFG_UPDATETYPE_JOIN_PROPOSAL); LfgRolesMap roles; roles[plr->GetGUIDLow()] = plr->GetLfgRoles(); AddToQueue(plr->GetGUID(), &roles, plr->GetLfgDungeons()); roles.clear(); } } /// /// Leave the lfg queue /// /// Player (could be NULL) /// Group (could be NULL) void LFGMgr::Leave(Player *plr, Group *grp /* = NULL*/) { uint64 guid = grp ? grp->GetGUID() : plr ? plr->GetGUID() : 0; ASSERT(guid); // Remove from Role Checks if (grp) { grp->SetLfgQueued(false); LfgRoleCheckMap::const_iterator itRoleCheck = m_RoleChecks.find(GUID_LOPART(guid)); if (itRoleCheck != m_RoleChecks.end()) { UpdateRoleCheck(grp); // No player to update role = LFG_ROLECHECK_ABORTED return; } } // Remove from queue RemoveFromQueue(guid); // Remove from Proposals for (LfgProposalMap::iterator it = m_Proposals.begin(); it != m_Proposals.end(); ++it) { // Mark the player/leader of group who left as didn't accept the proposal for (LfgProposalPlayerMap::iterator itPlayer = it->second->players.begin(); itPlayer != it->second->players.end(); ++itPlayer) if ((plr && itPlayer->first == plr->GetGUIDLow()) || (grp && itPlayer->first == GUID_LOPART(grp->GetLeaderGUID()))) itPlayer->second->accept = 0; } if (grp) { for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) if (Player *plrg = itr->getSource()) { plrg->GetSession()->SendLfgUpdateParty(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); plrg->GetLfgDungeons()->clear(); plrg->SetLfgRoles(ROLE_NONE); } } else { plr->GetSession()->SendLfgUpdatePlayer(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); plr->GetLfgDungeons()->clear(); plr->SetLfgRoles(ROLE_NONE); } } /// /// Given a Lfg group checks if leader needs to be show the popup to select more players /// /// Group than needs new players void LFGMgr::OfferContinue(Group *grp) { ASSERT(grp); if (grp->GetLfgStatus() != LFG_STATUS_COMPLETE) if (Player *leader = sObjectMgr.GetPlayer(grp->GetLeaderGUID())) leader->GetSession()->SendLfgOfferContinue(grp->GetLfgDungeonEntry(false)); } /// /// Creates a QueueInfo and adds it to the queue. Tries to match a group before joining. /// /// Player or group guid /// Player roles /// Selected dungeons void LFGMgr::AddToQueue(uint64 guid, LfgRolesMap *roles, LfgDungeonSet *dungeons) { ASSERT(roles); ASSERT(dungeons); LfgQueueInfo *pqInfo = new LfgQueueInfo(); pqInfo->joinTime = time_t(time(NULL)); for (LfgRolesMap::const_iterator it = roles->begin(); it != roles->end(); ++it) { if (pqInfo->tanks && it->second & ROLE_TANK) --pqInfo->tanks; else if (pqInfo->healers && it->second & ROLE_HEALER) --pqInfo->healers; else --pqInfo->dps; } for (LfgRolesMap::const_iterator itRoles = roles->begin(); itRoles != roles->end(); ++itRoles) pqInfo->roles[itRoles->first] = itRoles->second; // Expand random dungeons LfgDungeonSet *expandedDungeons = dungeons; if (isRandomDungeon(*dungeons->begin())) expandedDungeons = GetDungeonsByRandom(*dungeons->begin()); for (LfgDungeonSet::const_iterator it = expandedDungeons->begin(); it != expandedDungeons->end(); ++it) pqInfo->dungeons.insert(*it); m_QueueInfoMap[guid] = pqInfo; m_newToQueue.push_back(guid); } /// /// Removes the player/group from all queues /// /// Player or group guid /// bool bool LFGMgr::RemoveFromQueue(uint64 guid) { m_currentQueue.remove(guid); m_newToQueue.remove(guid); LfgQueueInfoMap::iterator it = m_QueueInfoMap.find(guid); if (it != m_QueueInfoMap.end()) { delete it->second; m_QueueInfoMap.erase(it); return true; } return false; } /// /// Check the queue to try to match groups. Returns all the posible matches /// /// Guids we trying to match with the rest of groups /// All guids in queue /// Proposals found. void LFGMgr::FindNewGroups(LfgGuidList &check, LfgGuidList all, LfgProposalList *proposals) { uint8 numPlayers = 0; uint8 numLfgGroups = 0; uint32 groupLowGuid = 0; LfgQueueInfoMap pqInfoMap; for (LfgGuidList::const_iterator it = check.begin(); it != check.end(); ++it) { if (!m_QueueInfoMap[*it]) { sLog.outError("LFGMgr::FindNewGroups: " UI64FMTD " is not queued but listed as queued!", *it); return; } pqInfoMap[*it] = m_QueueInfoMap[*it]; numPlayers += m_QueueInfoMap[*it]->roles.size(); if (IS_GROUP(*it)) { uint32 lowGuid = GUID_LOPART(*it); if (Group *grp = sObjectMgr.GetGroupByGUID(lowGuid)) if (grp->isLFGGroup()) { if (!numLfgGroups) groupLowGuid = lowGuid; ++numLfgGroups; } } } // Do not match groups already in a lfgDungeon if (numLfgGroups > 1 || numPlayers > MAXGROUPSIZE) { pqInfoMap.clear(); return; } if (numPlayers < MAXGROUPSIZE) { while (!all.empty()) { check.push_back(all.front()); all.pop_front(); FindNewGroups(check, all, proposals); check.pop_back(); } pqInfoMap.clear(); return; } // ----- Player checks ----- LfgRolesMap rolesMap; uint32 newLeaderLowGuid = 0; for (LfgQueueInfoMap::const_iterator it = pqInfoMap.begin(); it != pqInfoMap.end(); ++it) { for (LfgRolesMap::const_iterator itRoles = it->second->roles.begin(); itRoles != it->second->roles.end(); ++itRoles) { // Assign new leader if (itRoles->second & ROLE_LEADER && (!newLeaderLowGuid || urand(0, 1))) newLeaderLowGuid = itRoles->first; rolesMap[itRoles->first] = itRoles->second; } } if (rolesMap.size() != MAXGROUPSIZE) { sLog.outError("LFGMgr::FindNewGroups: There is a player multiple times in queue."); return; } Player *plr; PlayerSet players; for (LfgRolesMap::const_iterator it = rolesMap.begin(); it != rolesMap.end(); ++it) { plr = sObjectMgr.GetPlayer(it->first); for (PlayerSet::const_iterator itPlayer = players.begin(); itPlayer != players.end() && plr; ++itPlayer) { // Do not form a group with ignoring candidates if (plr->GetSocial()->HasIgnore((*itPlayer)->GetGUIDLow()) || (*itPlayer)->GetSocial()->HasIgnore(plr->GetGUIDLow())) plr = NULL; // neither with diferent faction if it's not a mixed faction server else if (plr->GetTeam() != (*itPlayer)->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) plr = NULL; } if (plr) players.insert(plr); } // if we dont have MAXGROUPSIZE (5) then we have self ignoring candidates or different faction groups // otherwise check if roles are compatible if (players.size() != MAXGROUPSIZE || !CheckGroupRoles(rolesMap)) { players.clear(); pqInfoMap.clear(); return; } // ----- Selected Dungeon checks ----- // Check if there are any compatible dungeon from the selected dungeons LfgDungeonSet *compatibleDungeons = new LfgDungeonSet(); bool compatibleDungeon; LfgQueueInfoMap::const_iterator itFirst = pqInfoMap.begin(); LfgQueueInfoMap::const_iterator itOther; LfgDungeonSet::const_iterator itDungeon; // Get the first group and compare with the others to select all common dungeons for (itDungeon = itFirst->second->dungeons.begin(); itDungeon != itFirst->second->dungeons.end(); ++itDungeon) { itOther = itFirst; compatibleDungeon = true; for (++itOther; itOther != pqInfoMap.end() && compatibleDungeon; ++itOther) if (itOther->second->dungeons.find(*itDungeon) == itOther->second->dungeons.end()) compatibleDungeon = false; if (compatibleDungeon) compatibleDungeons->insert(*itDungeon); } // now remove those with restrictions LfgLockStatusMap *pLockDungeons = GetGroupLockStatusDungeons(&players, compatibleDungeons); if (pLockDungeons) // Found dungeons not compatible, remove them from the set { LfgLockStatusSet *pLockSet = NULL; for (LfgLockStatusMap::const_iterator itLockMap = pLockDungeons->begin(); itLockMap != pLockDungeons->end() && compatibleDungeons->size(); ++itLockMap) { pLockSet = itLockMap->second; for(LfgLockStatusSet::const_iterator itLockSet = pLockSet->begin(); itLockSet != pLockSet->end(); ++itLockSet) { itDungeon = compatibleDungeons->find((*itLockSet)->dungeon); if (itDungeon != compatibleDungeons->end()) compatibleDungeons->erase(itDungeon); } pLockSet->clear(); delete pLockSet; } pLockDungeons->clear(); delete pLockDungeons; } pqInfoMap.clear(); // Any compatible dungeon after checking restrictions? if (!compatibleDungeons->size()) { delete compatibleDungeons; compatibleDungeons = NULL; players.clear(); return; } // Select a random dungeon from the compatible list itDungeon = compatibleDungeons->begin(); uint32 selectedDungeon = urand(0, compatibleDungeons->size() - 1); while (selectedDungeon > 0) { ++itDungeon; --selectedDungeon; } selectedDungeon = *itDungeon; compatibleDungeons->clear(); delete compatibleDungeons; // Create a new proposal LfgProposal *pProposal = new LfgProposal(selectedDungeon); pProposal->cancelTime = time_t(time(NULL)) + LFG_TIME_PROPOSAL; pProposal->queues = check; pProposal->groupLowGuid = groupLowGuid; // Assign new roles to players and assign new leader LfgProposalPlayer *ppPlayer; uint32 lowGuid; PlayerSet::const_iterator itPlayers = players.begin(); if (!newLeaderLowGuid) { uint8 pos = urand(0, players.size() - 1); for (uint8 i = 0; i < pos; ++i) ++itPlayers; newLeaderLowGuid = (*itPlayers)->GetGUIDLow(); } pProposal->leaderLowGuid = newLeaderLowGuid; for (itPlayers = players.begin(); itPlayers != players.end(); ++itPlayers) { lowGuid = (*itPlayers)->GetGUIDLow(); ppPlayer = new LfgProposalPlayer(); if ((*itPlayers)->GetGroup()) { ppPlayer->groupLowGuid = (*itPlayers)->GetGroup()->GetLowGUID(); if (ppPlayer->groupLowGuid == pProposal->groupLowGuid) // Player from existing group, autoaccept ppPlayer->accept = 1; } ppPlayer->role = rolesMap[lowGuid]; pProposal->players[lowGuid] = ppPlayer; } if (!proposals) proposals = new LfgProposalList(); proposals->push_back(pProposal); players.clear(); } /// /// Update the Role check info with the player selected role. /// /// Group /// Player (optional, default NULL) void LFGMgr::UpdateRoleCheck(Group *grp, Player *plr /* = NULL*/) { ASSERT(grp); uint32 rolecheckId = grp->GetLowGUID(); LfgRoleCheck *pRoleCheck = NULL; LfgRolesMap check_roles; LfgRoleCheckMap::iterator itRoleCheck = m_RoleChecks.find(rolecheckId); LfgDungeonSet *dungeons = plr->GetLfgDungeons(); bool newRoleCheck = itRoleCheck == m_RoleChecks.end(); if (newRoleCheck) { if (grp->GetLeaderGUID() != plr->GetGUID()) return; pRoleCheck = new LfgRoleCheck(); pRoleCheck->cancelTime = time_t(time(NULL)) + LFG_TIME_ROLECHECK; pRoleCheck->result = LFG_ROLECHECK_INITIALITING; pRoleCheck->leader = plr->GetGUIDLow(); for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) if (Player *plrg = itr->getSource()) pRoleCheck->roles[plrg->GetGUIDLow()] = 0; // Check if it's offer continue (random + current one) if (grp->isLFGGroup() && dungeons->size() == 2) { LfgDungeonSet::const_iterator itDungeon = dungeons->begin(); uint32 aDungeon = *itDungeon; uint32 rDungeon = *(++itDungeon); // it's a Offer to continue (Actual dungeon + random dungeon in the list) // Rolecheck will be using the actual dungeon - Players random if (aDungeon == grp->GetLfgDungeonEntry() && isRandomDungeon(rDungeon)) { for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { if (Player *plrg = itr->getSource()) { plrg->GetLfgDungeons()->clear(); plrg->GetLfgDungeons()->insert(rDungeon); } } pRoleCheck->dungeons.insert(aDungeon); } } if (pRoleCheck->dungeons.empty()) for (LfgDungeonSet::const_iterator itDungeon = plr->GetLfgDungeons()->begin(); itDungeon != plr->GetLfgDungeons()->end(); ++itDungeon) pRoleCheck->dungeons.insert(*itDungeon); } else pRoleCheck = itRoleCheck->second; LfgLockStatusMap *playersLockMap = NULL; if (plr) { // Player selected no role. if (plr->GetLfgRoles() < ROLE_TANK) pRoleCheck->result = LFG_ROLECHECK_NO_ROLE; else { // Check if all players have selected a role pRoleCheck->roles[plr->GetGUIDLow()] = plr->GetLfgRoles(); uint8 size = 0; for (LfgRolesMap::const_iterator itRoles = pRoleCheck->roles.begin(); itRoles != pRoleCheck->roles.end() && itRoles->second != ROLE_NONE; ++itRoles) ++size; if (pRoleCheck->roles.size() == size) { // use temporal var to check roles, CheckGroupRoles modifies the roles check_roles = pRoleCheck->roles; if (!CheckGroupRoles(check_roles)) // Group is not posible pRoleCheck->result = LFG_ROLECHECK_WRONG_ROLES; else { // Check if we can find a dungeon for that group pRoleCheck->result = LFG_ROLECHECK_FINISHED; if (pRoleCheck->dungeons.size() > 1) playersLockMap = GetPartyLockStatusDungeons(plr, &pRoleCheck->dungeons); else { LfgDungeonSet::const_iterator it = pRoleCheck->dungeons.begin(); LFGDungeonEntry const *dungeon = sLFGDungeonStore.LookupEntry(*it); if (dungeon && dungeon->type == LFG_TYPE_RANDOM) playersLockMap = GetPartyLockStatusDungeons(plr, GetDungeonsByRandom(*it)); else playersLockMap = GetPartyLockStatusDungeons(plr, &pRoleCheck->dungeons); } } } } } else pRoleCheck->result = LFG_ROLECHECK_ABORTED; WorldSession *session; WorldPacket data(SMSG_LFG_ROLE_CHECK_UPDATE, 4 + 1 + 1 + pRoleCheck->dungeons.size() * 4 + 1 + pRoleCheck->roles.size() * (8 + 1 + 4 + 1)); sLog.outDebug("SMSG_LFG_ROLE_CHECK_UPDATE"); BuildLfgRoleCheck(data, pRoleCheck); Player *plrg = NULL; for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { plrg = itr->getSource(); if (!plrg) continue; session = plrg->GetSession(); if (!newRoleCheck && plr) session->SendLfgRoleChosen(plr->GetGUID(), plr->GetLfgRoles()); session->SendPacket(&data); switch(pRoleCheck->result) { case LFG_ROLECHECK_INITIALITING: continue; case LFG_ROLECHECK_FINISHED: if (!playersLockMap) session->SendLfgUpdateParty(LFG_UPDATETYPE_ADDED_TO_QUEUE); else { if (grp->GetLeaderGUID() == plrg->GetGUID()) { uint32 size = 0; for (LfgLockStatusMap::const_iterator it = playersLockMap->begin(); it != playersLockMap->end(); ++it) size += 8 + 4 + it->second->size() * (4 + 4); WorldPacket data(SMSG_LFG_JOIN_RESULT, 4 + 4 + size); sLog.outDebug("SMSG_LFG_JOIN_RESULT"); data << uint32(LFG_JOIN_PARTY_NOT_MEET_REQS); // Check Result data << uint32(0); // Check Value (always 0 when PartyNotMeetReqs BuildPartyLockDungeonBlock(data, playersLockMap); session->SendPacket(&data); } session->SendLfgUpdateParty(LFG_UPDATETYPE_ROLECHECK_FAILED); plrg->GetLfgDungeons()->clear(); plrg->SetLfgRoles(ROLE_NONE); } break; default: if (grp->GetLeaderGUID() == plrg->GetGUID()) session->SendLfgJoinResult(LFG_JOIN_FAILED, pRoleCheck->result); session->SendLfgUpdateParty(LFG_UPDATETYPE_ROLECHECK_FAILED); plrg->GetLfgDungeons()->clear(); plrg->SetLfgRoles(ROLE_NONE); break; } } if (pRoleCheck->result == LFG_ROLECHECK_FINISHED) { grp->SetLfgQueued(true); AddToQueue(grp->GetGUID(), &pRoleCheck->roles, &pRoleCheck->dungeons); } if (pRoleCheck->result != LFG_ROLECHECK_INITIALITING) { pRoleCheck->dungeons.clear(); pRoleCheck->roles.clear(); delete pRoleCheck; if (!newRoleCheck) m_RoleChecks.erase(itRoleCheck); } else if (newRoleCheck) m_RoleChecks[rolecheckId] = pRoleCheck; } /// /// Check if a group can be formed with the given group /// /// Roles to check /// Used to remove ROLE_LEADER /// bool bool LFGMgr::CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag /*= true*/) { if (!groles.size()) return false; uint8 damage = 0; uint8 tank = 0; uint8 healer = 0; if (removeLeaderFlag) for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it) it->second &= ~ROLE_LEADER; for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it) { switch(it->second) { case ROLE_NONE: return false; case ROLE_TANK: if (tank == LFG_TANKS_NEEDED) return false; tank++; break; case ROLE_HEALER: if (healer == LFG_HEALERS_NEEDED) return false; healer++; break; case ROLE_DAMAGE: if (damage == LFG_DPS_NEEDED) return false; damage++; break; default: if (it->second & ROLE_TANK) { it->second -= ROLE_TANK; if (CheckGroupRoles(groles, false)) return true; it->second += ROLE_TANK; } if (it->second & ROLE_HEALER) { it->second -= ROLE_HEALER; if (CheckGroupRoles(groles, false)) return true; it->second += ROLE_HEALER; } if (it->second & ROLE_DAMAGE) { it->second -= ROLE_DAMAGE; return CheckGroupRoles(groles, false); } break; } } return true; } /// /// Update Proposal info with player answer /// /// Id of the proposal /// Player low guid /// Player answer void LFGMgr::UpdateProposal(uint32 proposalId, uint32 lowGuid, uint8 accept) { // Check if the proposal exists LfgProposalMap::iterator itProposal = m_Proposals.find(proposalId); if (itProposal == m_Proposals.end()) return; LfgProposal *pProposal = itProposal->second; // Check if proposal have the current player LfgProposalPlayer *ppPlayer = pProposal->players[lowGuid]; if (!ppPlayer) return; ppPlayer->accept = accept; if (!accept) { RemoveProposal(itProposal, LFG_UPDATETYPE_PROPOSAL_DECLINED); return; } LfgPlayerList players; Player *plr; // check if all have answered and reorder players (leader first) bool allAnswered = true; for (LfgProposalPlayerMap::const_iterator itPlayers = pProposal->players.begin(); itPlayers != pProposal->players.end(); ++itPlayers) { plr = sObjectMgr.GetPlayer(itPlayers->first); if (plr && itPlayers->first == pProposal->leaderLowGuid) players.push_front(plr); else players.push_back(plr); if (itPlayers->second->accept < 1) // No answer (-1) or not accepted (0) allAnswered = false; } if (!allAnswered) { for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) SendUpdateProposal(*it, proposalId, pProposal); } else { pProposal->state = LFG_PROPOSAL_SUCCESS; // Create a new group (if needed) Group *grp = sObjectMgr.GetGroupByGUID(pProposal->groupLowGuid); for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) { plr = *it; SendUpdateProposal(plr, proposalId, pProposal); //plr->SetLfgSendUpdates(false); if (plr->GetGroup()) { if (plr->GetGroup() != grp) plr->RemoveFromGroup(); plr->GetSession()->SendLfgUpdateParty(LFG_UPDATETYPE_GROUP_FOUND); } else plr->GetSession()->SendLfgUpdatePlayer(LFG_UPDATETYPE_GROUP_FOUND); if (!grp) { grp = new Group(); grp->Create(plr->GetGUID(), plr->GetName()); grp->ConvertToLFG(); sObjectMgr.AddGroup(grp); } else if (plr->GetGroup() != grp) grp->AddMember(plr->GetGUID(), plr->GetName()); grp->SetLfgRoles(plr->GetGUID(), pProposal->players[plr->GetGUIDLow()]->role); //plr->SetLfgSendUpdates(true); } // Set the dungeon difficulty LFGDungeonEntry const *dungeon = sLFGDungeonStore.LookupEntry(pProposal->dungeonId); ASSERT(dungeon); grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty)); grp->SetLfgDungeonEntry(dungeon->Entry()); grp->SetLfgStatus(LFG_STATUS_NOT_SAVED); grp->SendUpdate(); // Remove players/groups from Queue for (LfgGuidList::const_iterator it = pProposal->queues.begin(); it != pProposal->queues.end(); ++it) RemoveFromQueue(*it); // Teleport Player for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) TeleportPlayer(*it, false); for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) delete it->second; pProposal->players.clear(); pProposal->queues.clear(); delete pProposal; m_Proposals.erase(itProposal); } players.clear(); } /// /// Remove a proposal from the pool, remove the group that didn't accept (if needed) and readd the other members to the queue /// /// Proposal to remove /// Type of removal (LFG_UPDATETYPE_PROPOSAL_FAILED, LFG_UPDATETYPE_PROPOSAL_DECLINED) void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType type) { Player *plr; uint64 guid; LfgUpdateType updateType; LfgQueueInfoMap::iterator itQueue; LfgProposal *pProposal = itProposal->second; pProposal->state = LFG_PROPOSAL_FAILED; // Mark all people that didn't answered as no accept if (LFG_UPDATETYPE_PROPOSAL_FAILED) for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) if (it->second->accept < 1) it->second->accept = 0; // Inform players for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) { plr = sObjectMgr.GetPlayer(it->first); if (!plr) continue; guid = plr->GetGroup() ? plr->GetGroup()->GetGUID(): plr->GetGUID(); SendUpdateProposal(plr, itProposal->first, pProposal); if (!it->second->accept) // Remove player/player group from queues { updateType = type; plr->GetLfgDungeons()->clear(); plr->SetLfgRoles(ROLE_NONE); itQueue = m_QueueInfoMap.find(guid); if (itQueue != m_QueueInfoMap.end()) m_QueueInfoMap.erase(itQueue); } else // Readd to queue { m_newToQueue.push_back(guid); updateType = LFG_UPDATETYPE_ADDED_TO_QUEUE; } if (plr->GetGroup()) plr->GetSession()->SendLfgUpdateParty(updateType); else plr->GetSession()->SendLfgUpdatePlayer(updateType); } for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) delete it->second; pProposal->players.clear(); pProposal->queues.clear(); delete pProposal; m_Proposals.erase(itProposal); } /// /// Teleports the player in or out the dungeon /// /// Player /// Teleport out void LFGMgr::TeleportPlayer(Player *plr, bool out) { if (out) { plr->TeleportToBGEntryPoint(); return; } if (!plr->isAlive()) { plr->GetSession()->SendLfgTeleportError(LFG_TELEPORTERROR_PLAYER_DEAD); return; } if (plr->IsFalling() || plr->hasUnitState(UNIT_STAT_JUMPING)) { plr->GetSession()->SendLfgTeleportError(LFG_TELEPORTERROR_FALLING); return; } // TODO Add support for LFG_TELEPORTERROR_FATIGUE and LFG_TELEPORTERROR_INVALID_LOCATION if (Group *grp = plr->GetGroup()) if (LFGDungeonEntry const *dungeon = sLFGDungeonStore.LookupEntry(grp->GetLfgDungeonEntry())) if (AreaTrigger const* at = sObjectMgr.GetMapEntranceTrigger(dungeon->map)) { if (!plr->GetMap()->IsDungeon() && !plr->GetMap()->IsRaid()) plr->SetBattlegroundEntryPoint(); // TODO: Teleport to group plr->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation); } } // --------------------------------------------------------------------------// // Packet Functions // --------------------------------------------------------------------------// /// /// Build lfgRolecheck packet /// /// WorldPacket /// RoleCheck info void LFGMgr::BuildLfgRoleCheck(WorldPacket &data, LfgRoleCheck *pRoleCheck) { ASSERT(pRoleCheck); Player *plr; uint8 roles; data << uint32(pRoleCheck->result); // Check result data << uint8(pRoleCheck->result == LFG_ROLECHECK_INITIALITING); data << uint8(pRoleCheck->dungeons.size()); // Number of dungeons LFGDungeonEntry const *dungeon; for (LfgDungeonSet::iterator it = pRoleCheck->dungeons.begin(); it != pRoleCheck->dungeons.end(); ++it) { dungeon = sLFGDungeonStore.LookupEntry(*it); // not null - been checked at join time data << uint32(dungeon->Entry()); // Dungeon } data << uint8(pRoleCheck->roles.size()); // Players in group // Leader info MUST be sent 1st :S roles = pRoleCheck->roles[pRoleCheck->leader]; uint64 guid = MAKE_NEW_GUID(pRoleCheck->leader, 0, HIGHGUID_PLAYER); data << uint64(guid); // Guid data << uint8(roles > 0); // Ready data << uint32(roles); // Roles plr = sObjectMgr.GetPlayer(guid); data << uint8(plr ? plr->getLevel() : 0); // Level for (LfgRolesMap::const_iterator itPlayers = pRoleCheck->roles.begin(); itPlayers != pRoleCheck->roles.end(); ++itPlayers) { if (itPlayers->first == pRoleCheck->leader) continue; roles = itPlayers->second; guid = MAKE_NEW_GUID(itPlayers->first, 0, HIGHGUID_PLAYER); data << uint64(guid); // Guid data << uint8(roles > 0); // Ready data << uint32(roles); // Roles plr = sObjectMgr.GetPlayer(guid); data << uint8(plr ? plr->getLevel() : 0); // Level } } /// /// Build Proposal Update packet /// /// Player /// ProposalID /// Internal LFG Proposal /// void LFGMgr::SendUpdateProposal(Player *plr, uint32 proposalId, LfgProposal *pProp) { ASSERT(pProp); ASSERT(plr); uint32 pLogGuid = plr->GetGUIDLow(); LfgProposalPlayerMap::const_iterator itPlayer = pProp->players.find(pLogGuid); ASSERT(itPlayer != pProp->players.end()); // Player MUST be in the proposal LfgProposalPlayer *ppPlayer = itPlayer->second; uint32 pLowGroupGuid = ppPlayer->groupLowGuid; uint32 dLowGuid = pProp->groupLowGuid; uint32 dungeonId = pProp->dungeonId; sLog.outDebug("SMSG_LFG_PROPOSAL_UPDATE"); WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + pProp->players.size() * (4 + 1 + 1 + 1 + 1 +1)); if (!dLowGuid && plr->GetLfgDungeons()->size() == 1) // New group - select the dungeon the player selected dungeonId = *plr->GetLfgDungeons()->begin(); if (LFGDungeonEntry const *dungeon = sLFGDungeonStore.LookupEntry(dungeonId)) dungeonId = dungeon->Entry(); data << uint32(dungeonId); // Dungeon data << uint8(pProp->state); // Result state data << uint32(proposalId); // Internal Proposal ID data << uint32(0); // Bosses killed - FIXME data << uint8(pLowGroupGuid && pLowGroupGuid == dLowGuid); // Silent (show client window) data << uint8(pProp->players.size()); // Group size for (itPlayer = pProp->players.begin(); itPlayer != pProp->players.end(); ++itPlayer) { ppPlayer = itPlayer->second; data << uint32(ppPlayer->role); // Role data << uint8(itPlayer->first == pLogGuid); // Self player if (!ppPlayer->groupLowGuid) // Player not it a group { data << uint8(0); // Not in dungeon data << uint8(0); // Not same group } else { data << uint8(ppPlayer->groupLowGuid == dLowGuid); // In dungeon (silent) data << uint8(ppPlayer->groupLowGuid == pLowGroupGuid); // Same Group than player } data << uint8(ppPlayer->accept != -1); // Answered data << uint8(ppPlayer->accept == 1); // Accepted } plr->GetSession()->SendPacket(&data); } /// /// Build and Send LFG lock player info and reward /// /// Player void LFGMgr::SendLfgPlayerInfo(Player *plr) { uint32 rsize = 0; uint32 lsize = 0; bool done; LfgDungeonSet *randomlist = GetRandomDungeons(plr->getLevel(), plr->GetSession()->Expansion()); LfgLockStatusSet *lockSet = GetPlayerLockStatusDungeons(plr, GetAllDungeons()); if (randomlist) rsize = randomlist->size(); if (lockSet) lsize = lockSet->size(); sLog.outDebug("SMSG_LFG_PLAYER_INFO"); WorldPacket data(SMSG_LFG_PLAYER_INFO, 1 + rsize * (4 + 1 + 4 + 4 + 4 + 4 + 1 + 4 + 4 + 4) + 4 + lsize * (1 + 4 + 4 + 4 + 4 + 1 + 4 + 4 + 4)); if (!randomlist) data << uint8(0); else { LfgReward *reward; data << uint8(randomlist->size()); // Random Dungeon count for (LfgDungeonSet::iterator it = randomlist->begin(); it != randomlist->end(); ++it) { done = plr->isLfgDungeonDone(*it); reward = GetRandomDungeonReward(*it, done, plr->getLevel()); data << uint32(*it); // Entry data << uint8(done); data << uint32(reward->baseMoney); data << uint32(reward->baseXP); data << uint32(reward->variableMoney); data << uint32(reward->variableXP); data << uint8(reward->itemId != 0); if (reward->itemId) { data << uint32(reward->itemId); data << uint32(reward->displayId); data << uint32(reward->stackCount); } } randomlist->clear(); delete randomlist; } if (lockSet) BuildPlayerLockDungeonBlock(data, lockSet); plr->GetSession()->SendPacket(&data); } /// /// Build and Send LFG lock party info and reward /// /// Player void LFGMgr::SendLfgPartyInfo(Player *plr) { if (LfgLockStatusMap *lockMap = GetPartyLockStatusDungeons(plr, GetAllDungeons())) { uint32 size = 0; for (LfgLockStatusMap::const_iterator it = lockMap->begin(); it != lockMap->end(); ++it) size += 8 + 4 + it->second->size() * (4 + 4); sLog.outDebug("SMSG_LFG_PARTY_INFO"); WorldPacket data(SMSG_LFG_PARTY_INFO, 1 + size); BuildPartyLockDungeonBlock(data, lockMap); plr->GetSession()->SendPacket(&data); } } /// /// Build and Send LFG player reward /// /// Player void LFGMgr::SendLfgPlayerReward(Player *plr) { uint32 rdungeonId = 0; uint32 sdungeonId = 0; LFGDungeonEntry const *dungeon = sLFGDungeonStore.LookupEntry(*plr->GetLfgDungeons()->begin()); if (dungeon) rdungeonId = dungeon->Entry(); if (plr->GetGroup()) sdungeonId = plr->GetGroup()->GetLfgDungeonEntry(false); bool done = plr->isLfgDungeonDone(rdungeonId); LfgReward *reward = GetRandomDungeonReward(rdungeonId, done, plr->getLevel()); ASSERT(reward); uint8 itemNum = uint8(reward->itemId != 0); sLog.outDebug("SMSG_LFG_PLAYER_REWARD"); WorldPacket data(SMSG_LFG_PLAYER_REWARD, 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4 + 1 + itemNum * (4 + 4 + 4)); data << uint32(rdungeonId); // Random Dungeon Finished data << uint32(sdungeonId); // Dungeon Finished data << uint8(done); data << uint32(reward->strangers); data << uint32(reward->baseMoney); data << uint32(reward->baseXP); data << uint32(reward->variableMoney); data << uint32(reward->variableXP); data << uint8(itemNum); if (itemNum) { data << uint32(reward->itemId); data << uint32(reward->displayId); data << uint32(reward->stackCount); } plr->GetSession()->SendPacket(&data); } /// /// Build Party Dungeon lock status packet /// /// WorldPacket /// lock status map void LFGMgr::BuildPartyLockDungeonBlock(WorldPacket &data, LfgLockStatusMap *lockMap) { ASSERT(lockMap); data << uint8(lockMap->size()); LfgLockStatusSet *lockSet; for (LfgLockStatusMap::const_iterator it = lockMap->begin(); it != lockMap->end(); ++it) { lockSet = it->second; if (!lockSet) continue; data << uint64(MAKE_NEW_GUID(it->first, 0, HIGHGUID_PLAYER)); // Player guid BuildPlayerLockDungeonBlock(data, lockSet); } lockMap->clear(); delete lockMap; } /// /// Build Player Dungeon lock status packet /// /// WorldPacket /// lock status list void LFGMgr::BuildPlayerLockDungeonBlock(WorldPacket &data, LfgLockStatusSet *lockSet) { ASSERT(lockSet); data << uint32(lockSet->size()); // Size of lock dungeons for (LfgLockStatusSet::iterator it = lockSet->begin(); it != lockSet->end(); ++it) { data << uint32((*it)->dungeon); // Dungeon entry + type data << uint32((*it)->lockstatus); // Lock status delete (*it); } lockSet->clear(); delete lockSet; } // --------------------------------------------------------------------------// // Auxiliar Functions // --------------------------------------------------------------------------// /// /// Given a group get the dungeons that can't be done and reason /// /// Players to check lock status /// Dungeons to check /// LfgLockStatusMap* LfgLockStatusMap* LFGMgr::GetGroupLockStatusDungeons(PlayerSet *pPlayers, LfgDungeonSet *dungeons) { ASSERT(pPlayers); ASSERT(dungeons); LfgLockStatusSet *dungeonSet = NULL; LfgLockStatusMap *dungeonMap = new LfgLockStatusMap(); for (PlayerSet::const_iterator itr = pPlayers->begin(); itr != pPlayers->end(); ++itr) { dungeonSet = GetPlayerLockStatusDungeons(*itr, dungeons); if (dungeonSet) (*dungeonMap)[(*itr)->GetGUID()] = dungeonSet; } if (!dungeonMap->size()) { delete dungeonMap; dungeonMap = NULL; } return dungeonMap; } /// /// Get all Group members list of dungeons that can't be done and reason /// leader excluded as the list given is he list he can do /// /// Player to get Party Lock info /// Dungeons to check /// LfgLockStatusMap* LfgLockStatusMap* LFGMgr::GetPartyLockStatusDungeons(Player *plr, LfgDungeonSet *dungeons) { ASSERT(plr); ASSERT(dungeons); Group *grp = plr->GetGroup(); if (!grp) return NULL; PlayerSet *pPlayers = new PlayerSet(); Player *plrg; for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { plrg = itr->getSource(); if (plrg && plrg != plr) pPlayers->insert(plrg); } LfgLockStatusMap *dungeonMap = GetGroupLockStatusDungeons(pPlayers, dungeons); pPlayers->clear(); delete pPlayers; return dungeonMap; } /// /// Get list of dungeons player can't do and reasons /// /// Player to check lock status /// Dungeons to check /// LfgLockStatusSet* LfgLockStatusSet* LFGMgr::GetPlayerLockStatusDungeons(Player *plr, LfgDungeonSet *dungeons) { LfgLockStatusSet *list = new LfgLockStatusSet(); LfgLockStatus *lockstatus = NULL; LFGDungeonEntry const *dungeon; LfgLockStatusType locktype; uint8 level = plr->getLevel(); uint8 expansion = plr->GetSession()->Expansion(); AccessRequirement const* ar; for (LfgDungeonSet::const_iterator it = dungeons->begin(); it != dungeons->end(); ++it) { dungeon = sLFGDungeonStore.LookupEntry(*it); ASSERT(dungeon); // Will never happen - We provide a list from sLFGDungeonStore ar = sObjectMgr.GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty)); locktype = LFG_LOCKSTATUS_OK; if (dungeon->expansion > expansion) locktype = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION; else if (sDisableMgr.IsDisabledFor(DISABLE_TYPE_MAP, dungeon->map, plr)) locktype = LFG_LOCKSTATUS_RAID_LOCKED; else if (dungeon->difficulty > DUNGEON_DIFFICULTY_NORMAL && plr->GetBoundInstance(dungeon->map, Difficulty(dungeon->difficulty))) locktype = LFG_LOCKSTATUS_RAID_LOCKED; else { if (!sWorld.getConfig(CONFIG_INSTANCE_IGNORE_LEVEL)) if (dungeon->minlevel > level) locktype = LFG_LOCKSTATUS_TOO_LOW_LEVEL; else if (dungeon->maxlevel < level) locktype = LFG_LOCKSTATUS_TOO_HIGH_LEVEL; if (locktype == LFG_LOCKSTATUS_OK && ar) { if (ar->achievement && !plr->GetAchievementMgr().HasAchieved(sAchievementStore.LookupEntry(ar->achievement))) locktype = LFG_LOCKSTATUS_RAID_LOCKED; // FIXME: Check the correct lock value else if (plr->GetTeam() == ALLIANCE && ar->quest_A && !plr->GetQuestRewardStatus(ar->quest_A)) locktype = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; else if (plr->GetTeam() == HORDE && ar->quest_H && !plr->GetQuestRewardStatus(ar->quest_H)) locktype = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; else if (ar->item) { if (!plr->HasItemCount(ar->item, 1) && (!ar->item2 || !plr->HasItemCount(ar->item2, 1))) locktype = LFG_LOCKSTATUS_MISSING_ITEM; } else if (ar->item2 && !plr->HasItemCount(ar->item2, 1)) locktype = LFG_LOCKSTATUS_MISSING_ITEM; } } /* TODO VoA closed if WG is not under team control (LFG_LOCKSTATUS_RAID_LOCKED) locktype = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE; locktype = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE; locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL; locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL; locktype = LFG_LOCKSTATUS_NOT_IN_SEASON; // Need list of instances and needed season to open */ if (locktype != LFG_LOCKSTATUS_OK) { lockstatus = new LfgLockStatus(); lockstatus->dungeon = dungeon->Entry(); lockstatus->lockstatus = locktype; list->insert(lockstatus); } } if (!list->size()) { delete list; list = NULL; } return list; } /// /// Get the dungeon list that can be done. /// /// LfgDungeonSet* LfgDungeonSet* LFGMgr::GetAllDungeons() { LfgDungeonSet *alldungeons = m_CachedDungeonMap[0]; if (alldungeons) return alldungeons; LfgDungeonSet *dungeons; LFGDungeonEntry const *dungeon; alldungeons = new LfgDungeonSet(); m_CachedDungeonMap[0] = alldungeons; for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) { dungeon = sLFGDungeonStore.LookupEntry(i); if (!dungeon || dungeon->type == LFG_TYPE_ZONE) continue; dungeons = m_CachedDungeonMap[dungeon->grouptype]; if (!dungeons) { dungeons = new LfgDungeonSet(); m_CachedDungeonMap[dungeon->grouptype] = dungeons; } if (dungeon->type != LFG_TYPE_RANDOM) dungeons->insert(dungeon->ID); alldungeons->insert(dungeon->ID); } return alldungeons; } /// /// Get the dungeon list that can be done given a random dungeon entry. /// Special case: randomdungeon == 0 then will return all dungeons /// /// Random dungeon entry /// LfgDungeonSet* LfgDungeonSet* LFGMgr::GetDungeonsByRandom(uint32 randomdungeon) { uint8 groupType = 0; if (LFGDungeonEntry const *dungeon = sLFGDungeonStore.LookupEntry(randomdungeon)) groupType = dungeon->grouptype; return m_CachedDungeonMap[groupType]; } /// /// Get the random dungeon list that can be done at a certain level and expansion. /// /// Player level /// Player account expansion /// LfgDungeonSet* LfgDungeonSet* LFGMgr::GetRandomDungeons(uint8 level, uint8 expansion) { LfgDungeonSet *list = new LfgDungeonSet(); LFGDungeonEntry const *dungeon; for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) { dungeon = sLFGDungeonStore.LookupEntry(i); if (dungeon && dungeon->expansion <= expansion && dungeon->type == LFG_TYPE_RANDOM && dungeon->minlevel <= level && level <= dungeon->maxlevel) list->insert(dungeon->Entry()); } return list; } /// /// Get the reward of a given random dungeon at a certain level /// /// random dungeon id /// Dungeon previously done /// Player level /// LfgReward* LfgReward* LFGMgr::GetRandomDungeonReward(uint32 dungeon, bool done, uint8 level) { uint8 index = 0; switch((dungeon & 0x00FFFFFF)) // Get dungeon id from dungeon entry { case LFG_RANDOM_CLASSIC: if (level < 15) index = LFG_REWARD_LEVEL0; else if (level < 24) index = LFG_REWARD_LEVEL1; else if (level < 35) index = LFG_REWARD_LEVEL2; else if (level < 46) index = LFG_REWARD_LEVEL3; else if (level < 56) index = LFG_REWARD_LEVEL4; else index = LFG_REWARD_LEVEL5; break; case LFG_RANDOM_BC_NORMAL: index = LFG_REWARD_BC_NORMAL; break; case LFG_RANDOM_BC_HEROIC: index = LFG_REWARD_BC_HEROIC; break; case LFG_RANDOM_LK_NORMAL: index = level == 80 ? LFG_REWARD_LK_NORMAL80 : LFG_REWARD_LK_NORMAL; break; case LFG_RANDOM_LK_HEROIC: index = LFG_REWARD_LK_HEROIC; break; default: // This should never happen! done = false; index = LFG_REWARD_LEVEL0; sLog.outError("LFGMgr::GetRandomDungeonReward: Dungeon %u is not random dungeon!", dungeon); break; } return done ? m_RewardDoneList.at(index) : m_RewardList.at(index); } /// /// Given a Dungeon id returns the dungeon Group Type /// /// Dungeon id /// uint8: GroupType uint8 LFGMgr::GetDungeonGroupType(uint32 dungeonId) { LFGDungeonEntry const *dungeon = sLFGDungeonStore.LookupEntry(dungeonId); if (!dungeon) return 0; return dungeon->grouptype; } /// /// Given a Dungeon id returns if it's random /// /// Dungeon id /// bool bool LFGMgr::isRandomDungeon(uint32 dungeonId) { LFGDungeonEntry const *dungeon = sLFGDungeonStore.LookupEntry(dungeonId); if (!dungeon) return false; return dungeon->type == LFG_TYPE_RANDOM; }