/* * 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 "LFGMgr.h" #include "ObjectMgr.h" #include "WorldPacket.h" /*********************************************************/ /*** LFG QUEUES ***/ /*********************************************************/ LFGQueue::LFGQueue() { m_LfgQueue.clear(); avgWaitTime = -1; waitTimeTanks = -1; waitTimeHealer = -1; waitTimeDps = -1; } LFGQueue::~LFGQueue() { m_LfgQueue.clear(); } void LFGQueue::AddToQueue(uint64 guid, LfgQueueInfo* pqInfo) { delete m_LfgQueue[guid]; m_LfgQueue[guid] = pqInfo; // ATM will only add it to the queue... No find group implementation... yet (on purpose) } bool LFGQueue::RemoveFromQueue(uint64 guid) { if (m_LfgQueue.empty()) return false; LfgQueueInfoMap::iterator itr = m_LfgQueue.find(guid); if (itr == m_LfgQueue.end()) return false; delete itr->second; m_LfgQueue.erase(itr); return true; } LfgQueueInfo* LFGQueue::GetQueueInfo(uint64 guid) { return m_LfgQueue[guid]; } void LFGQueue::Update() { if (m_LfgQueue.empty()) return; Player *plr; LfgQueueInfo *queue; time_t currTime = time(NULL); uint32 queuedTime; uint8 role = 0; int32 waitTime = -1; for (LfgQueueInfoMap::const_iterator itQueue = m_LfgQueue.begin(); itQueue != m_LfgQueue.end(); ++itQueue) { queue = itQueue->second; // Update queue status queuedTime = uint32(currTime - queue->joinTime); for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) { plr = objmgr.GetPlayer(itPlayer->first); if (!plr) continue; role = itPlayer->second; if (role & ROLE_TANK) { if (role & ROLE_HEALER || role & ROLE_DAMAGE) waitTime = avgWaitTime; else waitTime = waitTimeTanks; } else if (role & ROLE_HEALER) { if (role & ROLE_DAMAGE) waitTime = avgWaitTime; else waitTime = waitTimeDps; } plr->GetSession()->SendLfgQueueStatus(queue->dungeonId, waitTime, avgWaitTime, waitTimeTanks, waitTimeHealer, waitTimeDps, queuedTime, queue->tanks, queue->healers, queue->dps); } } } LFGMgr::LFGMgr() { m_QueueTimer = 0; m_update = true; } LFGMgr::~LFGMgr() { // RewardList to be removed -> query quest system for (LfgRewardList::iterator it = m_RewardList.begin(); it != m_RewardList.end(); ++it) delete *it; m_RewardList.clear(); // 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(LFGQueueMap::iterator it = m_Queues.begin(); it != m_Queues.end(); ++it) delete it->second; m_Queues.clear(); for (LfgDungeonMap::iterator it = m_DungeonsMap.begin(); it != m_DungeonsMap.end(); ++it) { it->second->clear(); delete it->second; } m_DungeonsMap.clear(); for (LfgRoleCheckMap::iterator it = m_RoleChecks.begin(); it != m_RoleChecks.end(); ++it) delete it->second; m_RoleChecks.clear(); } void LFGMgr::Update(uint32 diff) { if (!m_update) return; // Update all players status queue info if (m_QueueTimer > LFG_QUEUEUPDATE_INTERVAL) { m_QueueTimer = 0; for (LFGQueueMap::iterator it = m_Queues.begin(); it != m_Queues.end(); ++it) it->second->Update(); } else m_QueueTimer += diff; 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 = objmgr.GetPlayer(itRoles->first); if (!plr) continue; plr->GetSession()->SendPacket(&data); plr->m_lookingForGroup.applyDungeons.clear(); plr->m_lookingForGroup.roles = 0; if (itRoles->first == pRoleCheck->leader) plr->GetSession()->SendLfgJoinResult(LFG_JOIN_FAILED, pRoleCheck->result); } delete pRoleCheck; m_RoleChecks.erase(itRoleCheck); } } /// /// 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 m_DungeonsMap[LFG_ALL_DUNGEONS] = GetAllDungeons(); m_DungeonsMap[LFG_RANDOM_CLASSIC] = GetDungeonsByRandom(LFG_RANDOM_CLASSIC); m_DungeonsMap[LFG_RANDOM_BC_NORMAL] = GetDungeonsByRandom(LFG_RANDOM_BC_NORMAL); m_DungeonsMap[LFG_RANDOM_BC_HEROIC] = GetDungeonsByRandom(LFG_RANDOM_BC_HEROIC); m_DungeonsMap[LFG_RANDOM_LK_NORMAL] = GetDungeonsByRandom(LFG_RANDOM_LK_NORMAL); m_DungeonsMap[LFG_RANDOM_LK_HEROIC] = GetDungeonsByRandom(LFG_RANDOM_LK_HEROIC); } /// /// Adds the player to lfg queue /// /// Player void LFGMgr::Join(Player *plr) { Group *grp = plr->GetGroup(); // TODO - 2010-05-27 Anyone can init rolecheck when already in a LFD Group? if (grp && grp->GetLeaderGUID() != plr->GetGUID()) return; // Previous checks before joining LfgJoinResult result = LFG_JOIN_OK; 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->m_lookingForGroup.applyDungeons.begin(); it != plr->m_lookingForGroup.applyDungeons.end(); ++it) { if ((m_DungeonsMap[LFG_ALL_DUNGEONS])->find(*it) == (m_DungeonsMap[LFG_ALL_DUNGEONS])->end()) { result = LFG_JOIN_DUNGEON_INVALID; break; } } } if (grp && result == LFG_JOIN_OK) { if (grp->GetMembersCount() > MAXGROUPSIZE) result = LFG_JOIN_TOO_MUCH_MEMBERS; else if(grp->isRaidGroup()) result = LFG_JOIN_MIXED_RAID_DUNGEON; else { for (GroupReference *itr = grp->GetFirstMember(); itr != NULL && result == LFG_JOIN_OK; itr = itr->next()) { if (Player *plrg = itr->getSource()) { if (plrg->HasAura(LFG_SPELL_DESERTER)) result = LFG_JOIN_PARTY_DESERTER; else if (plrg->HasAura(LFG_SPELL_COOLDOWN)) result = LFG_JOIN_PARTY_RANDOM_COOLDOWN; } else result = LFG_JOIN_DISCONNECTED; } } } if (result != LFG_JOIN_OK) { plr->m_lookingForGroup.applyDungeons.clear(); plr->m_lookingForGroup.roles = 0; plr->GetSession()->SendLfgJoinResult(result, 0); return; } if (grp) { Player *plrg = NULL; for (GroupReference *itr = plr->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { plrg = itr->getSource(); // Not null, checked earlier plrg->m_lookingForGroup.applyDungeons = plr->m_lookingForGroup.applyDungeons; 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); // Add player to queue LfgQueueInfo *pqInfo; uint8 groupType = 0; uint8 tanks = LFG_TANKS_NEEDED; uint8 healers = LFG_HEALERS_NEEDED; uint8 dps = LFG_DPS_NEEDED; if (plr->m_lookingForGroup.roles & ROLE_TANK) --tanks; else if (plr->m_lookingForGroup.roles & ROLE_HEALER) --healers; else --dps; m_update = false; for (LfgDungeonSet::const_iterator it = plr->m_lookingForGroup.applyDungeons.begin(); it != plr->m_lookingForGroup.applyDungeons.end(); ++it) { groupType = GetDungeonGroupType(*it); pqInfo = m_Queues[groupType] ? m_Queues[groupType]->GetQueueInfo(plr->GetGUID()) : NULL; // if exist we have already added the player with another dungeon sharing same GroupType if (pqInfo) continue; pqInfo = new LfgQueueInfo(); pqInfo->dungeonId = *it; pqInfo->joinTime = time_t(time(NULL)); pqInfo->tanks = tanks; pqInfo->healers = healers; pqInfo->dps = dps; pqInfo->roles[plr->GetGUID()] = plr->m_lookingForGroup.roles; if (!m_Queues[groupType]) m_Queues[groupType] = new LFGQueue(); m_Queues[groupType]->AddToQueue(plr->GetGUID(), pqInfo); } m_update = true; } } /// /// 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); // Check if player was in a rolecheck if (grp) { LfgRoleCheckMap::iterator itRoleCheck = m_RoleChecks.find(GUID_LOPART(grp->GetGUID())); if (itRoleCheck != m_RoleChecks.end()) { UpdateRoleCheck(grp); // No player to update role = LFG_ROLECHECK_ABORTED return; } } // Check if player/group was in the queue bool inQueue = false; for (LFGQueueMap::iterator it = m_Queues.begin(); it != m_Queues.end(); ++it) inQueue |= it->second->RemoveFromQueue(guid); // Not in queue if (!inQueue) return; 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->m_lookingForGroup.applyDungeons.clear(); plrg->m_lookingForGroup.roles = 0; } } else { plr->GetSession()->SendLfgUpdatePlayer(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); plr->m_lookingForGroup.applyDungeons.clear(); plr->m_lookingForGroup.roles = 0; } } /// /// Update the Role check info with the player selected role. /// /// Group /// Player void LFGMgr::UpdateRoleCheck(Group *grp, Player *plr /* = NULL*/) { assert(grp); uint32 rolecheckId = GUID_LOPART(grp->GetGUID()); LfgRoleCheck *pRoleCheck = NULL; LfgRolesMap check_roles; LfgRoleCheckMap::iterator itRoleCheck = m_RoleChecks.find(rolecheckId); bool newRoleCheck = itRoleCheck == m_RoleChecks.end(); if (newRoleCheck) { if (!grp->IsLeader(plr->GetGUID())) return; pRoleCheck = new LfgRoleCheck(); pRoleCheck->cancelTime = time_t(time(NULL)) + LFG_TIME_ROLECHECK; pRoleCheck->result = LFG_ROLECHECK_INITIALITING; pRoleCheck->leader = plr->GetGUID(); for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) if (Player *plrg = itr->getSource()) pRoleCheck->roles[plrg->GetGUID()] = 0; pRoleCheck->dungeons = plr->m_lookingForGroup.applyDungeons; } else pRoleCheck = itRoleCheck->second; LfgLockStatusMap *playersLockMap = NULL; if (plr) { // Player selected no role. if (plr->m_lookingForGroup.roles < ROLE_TANK) pRoleCheck->result = LFG_ROLECHECK_NO_ROLE; else { // Check if all players have selected a role pRoleCheck->roles[plr->GetGUID()] = plr->m_lookingForGroup.roles; 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 for (LfgRolesMap::const_iterator itRoles = pRoleCheck->roles.begin(); itRoles != pRoleCheck->roles.end(); ++itRoles) check_roles[itRoles->first] = itRoles->second; 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, m_DungeonsMap[*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->m_lookingForGroup.roles); 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->IsLeader(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->m_lookingForGroup.applyDungeons.clear(); plrg->m_lookingForGroup.roles = 0; } break; default: if (grp->IsLeader(plrg->GetGUID())) session->SendLfgJoinResult(LFG_JOIN_FAILED, pRoleCheck->result); session->SendLfgUpdateParty(LFG_UPDATETYPE_ROLECHECK_FAILED); plrg->m_lookingForGroup.applyDungeons.clear(); plrg->m_lookingForGroup.roles = 0; break; } } if (pRoleCheck->result == LFG_ROLECHECK_FINISHED) { // Add qroup to queue LfgQueueInfo *pqInfo; uint8 groupType = 0; uint8 tanks = LFG_TANKS_NEEDED; uint8 healers = LFG_HEALERS_NEEDED; uint8 dps = LFG_DPS_NEEDED; for (LfgRolesMap::const_iterator it = check_roles.begin(); it != check_roles.end(); ++it) { if (it->second & ROLE_TANK) --tanks; else if (it->second & ROLE_HEALER) --healers; else --dps; } uint64 guid = grp->GetGUID(); m_update = false; for (LfgDungeonSet::const_iterator it = pRoleCheck->dungeons.begin(); it != pRoleCheck->dungeons.end(); ++it) { groupType = GetDungeonGroupType(*it); pqInfo = m_Queues[groupType] ? m_Queues[groupType]->GetQueueInfo(guid) : NULL; // if exist we have already added the player with another dungeon sharing same GroupType if (pqInfo) continue; pqInfo = new LfgQueueInfo(); pqInfo->dungeonId = *it; pqInfo->joinTime = time_t(time(NULL)); pqInfo->tanks = tanks; pqInfo->healers = healers; pqInfo->dps = dps; for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { if (Player *plrg = itr->getSource()) pqInfo->roles[plrg->GetGUID()] = plrg->m_lookingForGroup.roles; } if (!m_Queues[groupType]) m_Queues[groupType] = new LFGQueue(); m_Queues[groupType]->AddToQueue(guid, pqInfo); } m_update = true; } if (pRoleCheck->result != LFG_ROLECHECK_INITIALITING) { 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 /// /// Map of roles /// bool, will be 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; uint64 tguid = 0; uint64 hguid = 0; uint64 dguid = 0; uint64 guid = 0; uint8 role = 0; if (removeLeaderFlag) for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it) it->second &= ~ROLE_LEADER; for (LfgRolesMap::const_iterator it = groles.begin(); it != groles.end(); ++it) { guid = it->first; role = it->second; if (role == ROLE_NONE) return false; if (role & ROLE_TANK) { if (!tank) { tguid = guid; ++tank; } else { if (groles[tguid] == ROLE_TANK) tguid = guid; groles[tguid] -= ROLE_TANK; return CheckGroupRoles(groles, false); } } if (role & ROLE_HEALER) { if (!healer) { hguid = guid; ++healer; } else { if (groles[hguid] == ROLE_HEALER) hguid = guid; groles[hguid] -= ROLE_HEALER; return CheckGroupRoles(groles, false); } } if (role & ROLE_DAMAGE) { if (damage < 3) { if (!damage) dguid = guid; ++damage; } else { if (groles[dguid] == ROLE_DAMAGE) dguid = guid; groles[dguid] -= ROLE_DAMAGE; if (!CheckGroupRoles(groles, false)) groles[dguid] += ROLE_DAMAGE; else return true; } } } return true; } // --------------------------------------------------------------------------// // Packet Functions // --------------------------------------------------------------------------// /// /// Build lfgRolecheck packet /// /// WorldPacket /// Player /// Player status in LFG system 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]; data << uint64(pRoleCheck->leader); // Guid data << uint8(roles > 0); // Ready data << uint32(roles); // Roles plr = objmgr.GetPlayer(pRoleCheck->leader); if (plr) data << uint8(plr->getLevel()); // Level else data << uint8(0); for (LfgRolesMap::const_iterator itPlayers = pRoleCheck->roles.begin(); itPlayers != pRoleCheck->roles.end(); ++itPlayers) { if (itPlayers->first == pRoleCheck->leader) continue; roles = itPlayers->second; data << uint64(itPlayers->first); // Guid data << uint8(roles > 0); // Ready data << uint32(roles); // Roles plr = objmgr.GetPlayer(pRoleCheck->leader); if (plr) data << uint8(plr->getLevel()); // Level else data << uint8(0); } } /// /// Build and Send LFG lock player info and reward /// /// Player void LFGMgr::SendLfgPlayerInfo(Player *plr) { uint32 rsize = 0; uint32 lsize = 0; LfgDungeonSet *randomlist = GetRandomDungeons(plr->getLevel(), plr->GetSession()->Expansion()); LfgLockStatusSet *lockSet = GetPlayerLockStatusDungeons(plr, m_DungeonsMap[LFG_ALL_DUNGEONS]); 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 * (4 + 4)); if (!randomlist) data << uint8(0); else { data << uint8(randomlist->size()); // Random Dungeon count for (LfgDungeonSet::iterator it = randomlist->begin(); it != randomlist->end(); ++it) { data << uint32(*it); // Entry BuildRewardBlock(data, *it, plr); } randomlist->clear(); delete randomlist; } 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, m_DungeonsMap[LFG_ALL_DUNGEONS])) { 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 Reward packet structure for a given dungeon /// /// WorldPacket /// Dungeon entry /// Player void LFGMgr::BuildRewardBlock(WorldPacket &data, uint32 dungeon, Player *plr) { bool done = plr->m_lookingForGroup.isDungeonDone(dungeon); LfgReward *reward = GetRandomDungeonReward(dungeon, done, plr->getLevel()); if (!reward) return; data << uint8(done); if (data.GetOpcode() == SMSG_LFG_PLAYER_REWARD) data << uint32(reward->strangers); 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); } } /// /// 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; uint64 guid; for (LfgLockStatusMap::const_iterator it = lockMap->begin(); it != lockMap->end(); ++it) { guid = it->first; lockSet = it->second; if (!lockSet) continue; data << uint64(guid); // 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 // --------------------------------------------------------------------------// /// /// 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 /// /// Group /// Dungeons to check /// LfgLockStatusMap* LfgLockStatusMap* LFGMgr::GetPartyLockStatusDungeons(Player *plr, LfgDungeonSet *dungeons) { assert(plr); assert(dungeons); Group *grp = plr->GetGroup(); if (!grp) return NULL; Player *plrg; LfgLockStatusSet *dungeonSet = NULL; LfgLockStatusMap *dungeonMap = new LfgLockStatusMap(); for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { plrg = itr->getSource(); if (!plrg || plrg == plr) continue; dungeonSet = GetPlayerLockStatusDungeons(plrg, dungeons); if (dungeonSet) (*dungeonMap)[plrg->GetGUID()] = dungeonSet; } if (!dungeonMap->size()) { delete dungeonMap; dungeonMap = NULL; } return dungeonMap; } /// /// Get list of dungeons player can't do and reasons /// /// Player /// 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(); 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 locktype = LFG_LOCKSTATUS_OK; if (dungeon->expansion > expansion) locktype = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION; else if (dungeon->minlevel > level) locktype = LFG_LOCKSTATUS_TOO_LOW_LEVEL; else if (dungeon->maxlevel < level) locktype = LFG_LOCKSTATUS_TOO_HIGH_LEVEL; /* TODO - Use these types when needed... else if () locktype = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE; else if () locktype = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE; else if () // Locked due to WG, closed by GM, done daily, etc locktype = LFG_LOCKSTATUS_RAID_LOCKED; else if () locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL; else if () locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL; else if () // Need list of instances and needed quest to enter locktype = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; else if () // Need list of instances and needed key to enter locktype = LFG_LOCKSTATUS_MISSING_ITEM; else if () // Need list of instances and needed season to open locktype = LFG_LOCKSTATUS_NOT_IN_SEASON; */ 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 *dungeons = new LfgDungeonSet(); LFGDungeonEntry const *dungeon; for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) { dungeon = sLFGDungeonStore.LookupEntry(i); if (!dungeon || dungeon->type == LFG_TYPE_ZONE) continue; dungeons->insert(dungeon->ID); } if (!dungeons->size()) { delete dungeons; return NULL; } else return dungeons; } /// /// Get the dungeon list that can be done given a random dungeon entry. /// /// Random dungeon entry /// LfgDungeonSet* LfgDungeonSet* LFGMgr::GetDungeonsByRandom(uint32 randomdungeon) { LFGDungeonEntry const *dungeon = sLFGDungeonStore.LookupEntry(randomdungeon); if (!dungeon) return NULL; uint32 grouptype = dungeon->grouptype; LfgDungeonSet *random = new LfgDungeonSet(); for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) { dungeon = sLFGDungeonStore.LookupEntry(i); if (!dungeon || dungeon->type == LFG_TYPE_RANDOM || dungeon->grouptype != grouptype) continue; random->insert(dungeon->ID); } if (!random->size()) { delete random; return NULL; } else return random; } /// /// 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 /// /// random dungeon id /// Dungeon previously done /// 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; }