diff options
author | Spp <spp@jorge.gr> | 2010-08-11 15:55:27 +0200 |
---|---|---|
committer | Spp <spp@jorge.gr> | 2010-08-11 15:55:27 +0200 |
commit | 41534a13247aef321d555e19d08bf9a2b25a0fbd (patch) | |
tree | 3c1d2886a1fd047d47b7a77d1638e5e16e08e1c4 | |
parent | f9468e7e5d13071d170b4e10a4f20689e2ae7dc3 (diff) |
Dungeon Finder: Add proposals, find group algorithm and teleport support
Note: Adds hability to find a group and be teleported to dungeon, but no rewards yet (WIP)
--HG--
branch : trunk
-rw-r--r-- | src/server/game/DungeonFinding/LFGMgr.cpp | 582 | ||||
-rw-r--r-- | src/server/game/DungeonFinding/LFGMgr.h | 73 | ||||
-rw-r--r-- | src/server/game/Groups/Group.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Groups/Group.h | 3 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Handlers/GroupHandler.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Handlers/LFGHandler.cpp | 21 |
6 files changed, 682 insertions, 3 deletions
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 0e21afd5c9d..4830e61ee83 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -33,6 +33,7 @@ LFGMgr::LFGMgr() m_waitTimeHealer = -1; m_waitTimeDps = -1; m_update = true; + m_lfgProposalId = 1; } LFGMgr::~LFGMgr() @@ -51,6 +52,10 @@ LFGMgr::~LFGMgr() 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(); @@ -102,6 +107,63 @@ void LFGMgr::Update(uint32 diff) 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) { @@ -323,6 +385,15 @@ void LFGMgr::Leave(Player *plr, Group *grp /* = NULL*/) // 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()) @@ -342,6 +413,19 @@ void LFGMgr::Leave(Player *plr, Group *grp /* = NULL*/) } /// <summary> +/// Given a Lfg group checks if leader needs to be show the popup to select more players +/// </summary> +/// <param name="Group *">Group than needs new players</param> +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)); +} + +/// <summary> /// Creates a QueueInfo and adds it to the queue. Tries to match a group before joining. /// </summary> /// <param name="uint64">Player or group guid</param> @@ -398,6 +482,211 @@ bool LFGMgr::RemoveFromQueue(uint64 guid) } /// <summary> +/// Check the queue to try to match groups. Returns all the posible matches +/// </summary> +/// <param name="LfgGuidList &">Guids we trying to match with the rest of groups</param> +/// <param name="LfgGuidList">All guids in queue</param> +/// <param name="LfgProposalList *">Proposals found.</param> +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(); +} + +/// <summary> /// Update the Role check info with the player selected role. /// </summary> /// <param name="Group *">Group</param> @@ -612,6 +901,210 @@ bool LFGMgr::CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag /*= true return tank == LFG_TANKS_NEEDED && healer == LFG_HEALERS_NEEDED && damage == LFG_DPS_NEEDED; } +/// <summary> +/// Update Proposal info with player answer +/// </summary> +/// <param name="uint32">Id of the proposal</param> +/// <param name="uint32">Player low guid</param> +/// <param name="uint8">Player answer</param> +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(); +} + +/// <summary> +/// Remove a proposal from the pool, remove the group that didn't accept (if needed) and readd the other members to the queue +/// </summary> +/// <param name="LfgProposalMap::iterator">Proposal to remove</param> +/// <param name="LfgUpdateType">Type of removal (LFG_UPDATETYPE_PROPOSAL_FAILED, LFG_UPDATETYPE_PROPOSAL_DECLINED)</param> +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); +} + +/// <summary> +/// Teleports the player in or out the dungeon +/// </summary> +/// <param name="Player *">Player</param> +/// <param name="bool">Teleport out</param> +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 // --------------------------------------------------------------------------// @@ -664,6 +1157,57 @@ void LFGMgr::BuildLfgRoleCheck(WorldPacket &data, LfgRoleCheck *pRoleCheck) } /// <summary> +/// Build Proposal Update packet +/// </summary> +/// <param name="Player *">Player</param> +/// <param name="uint32">ProposalID</param> +/// <param name="LfgProposal *">Internal LFG Proposal</param> +/// <returns></returns> +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; + + 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 (plr->GetLfgDungeons()->size() == 1 && *plr->GetLfgDungeons()->begin() != pProp->dungeonId) + data << uint32(*plr->GetLfgDungeons()->begin()); // Random dungeon + else + data << uint32(pProp->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); +} + +/// <summary> /// Build and Send LFG lock player info and reward /// </summary> /// <param name="Player *">Player</param> @@ -733,6 +1277,44 @@ void LFGMgr::SendLfgPartyInfo(Player *plr) } /// <summary> +/// Build and Send LFG player reward +/// </summary> +/// <param name="Player *">Player</param> +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); +} + +/// <summary> /// Build Party Dungeon lock status packet /// </summary> /// <param name="WorldPacket &">WorldPacket</param> diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h index effc9bd76d8..84229166a79 100644 --- a/src/server/game/DungeonFinding/LFGMgr.h +++ b/src/server/game/DungeonFinding/LFGMgr.h @@ -27,12 +27,15 @@ enum LFGenum { LFG_TIME_ROLECHECK = 2*MINUTE, + LFG_TIME_PROPOSAL = 2*MINUTE, + LFG_VOTES_NEEDED = 3, LFG_TANKS_NEEDED = 1, LFG_HEALERS_NEEDED = 1, LFG_DPS_NEEDED = 3, LFG_QUEUEUPDATE_INTERVAL = 15000, LFG_SPELL_COOLDOWN = 71328, LFG_SPELL_DESERTER = 71041, + LFG_MAX_KICKS = 3, }; enum LfgType @@ -45,6 +48,13 @@ enum LfgType LFG_TYPE_RANDOM = 6, }; +enum LfgProposalState +{ + LFG_PROPOSAL_INITIATING = 0, + LFG_PROPOSAL_FAILED = 1, + LFG_PROPOSAL_SUCCESS = 2, +}; + enum LfgGroupType { LFG_GROUPTYPE_ALL = 0, // Internal use, represents all groups. @@ -75,6 +85,19 @@ enum LfgLockStatusType LFG_LOCKSTATUS_NOT_IN_SEASON = 1031, }; +enum LfgTeleportError +{ + //LFG_TELEPORTERROR_UNK1 = 0, // No reaction + LFG_TELEPORTERROR_PLAYER_DEAD = 1, + LFG_TELEPORTERROR_FALLING = 2, + //LFG_TELEPORTERROR_UNK2 = 3, // You can't do that right now + LFG_TELEPORTERROR_FATIGUE = 4, + //LFG_TELEPORTERROR_UNK3 = 5, // No reaction + LFG_TELEPORTERROR_INVALID_LOCATION = 6, + //LFG_TELEPORTERROR_UNK4 = 7, // You can't do that right now + //LFG_TELEPORTERROR_UNK5 = 8, // You can't do that right now +}; + enum LfgJoinResult { LFG_JOIN_OK = 0, // Joined (no client msg) @@ -135,6 +158,13 @@ enum LfgRewardEnums LFG_REWARD_DATA_SIZE = 10, }; +enum LfgDungeonStatus +{ + LFG_STATUS_SAVED = 0, + LFG_STATUS_NOT_SAVED = 1, + LFG_STATUS_COMPLETE = 2, +}; + const uint32 RewardDungeonData[LFG_REWARD_DATA_SIZE+1][5] = { // XP, money, item, item display, count {310, 3500, 51999, 56915, 1}, // Classic 15-23 @@ -201,6 +231,38 @@ struct LfgQueueInfo LfgRolesMap roles; // Selected Player Role/s }; +struct LfgProposalPlayer +{ + LfgProposalPlayer(): role(0), accept(-1), groupLowGuid(0) {}; + uint8 role; // Proposed role + int8 accept; // Accept status (-1 not answer | 0 Not agree | 1 agree) + uint32 groupLowGuid; // Original group guid (Low guid) 0 if no original group +}; + +typedef std::map<uint32, LfgProposalPlayer*> LfgProposalPlayerMap; + +// Stores all Dungeon Proposal after matching candidates +struct LfgProposal +{ + LfgProposal(uint32 dungeon): state(LFG_PROPOSAL_INITIATING), groupLowGuid(0), dungeonId(dungeon), leaderLowGuid(0) {} + + ~LfgProposal() + { + for (LfgProposalPlayerMap::iterator it = players.begin(); it != players.end(); ++it) + delete it->second; + players.clear(); + queues.clear(); + }; + uint32 dungeonId; // Dungeon to join + LfgProposalState state; // State of the proposal + uint32 groupLowGuid; // Proposal group (0 if new) + uint32 leaderLowGuid; // Leader guid. + time_t cancelTime; // Time when we will cancel this proposal + LfgGuidList queues; // Queue Ids to remove/readd + LfgProposalPlayerMap players; // Player current groupId + +}; + // Stores all rolecheck info of a group that wants to join LFG struct LfgRoleCheck { @@ -215,9 +277,11 @@ typedef std::set<Player*> PlayerSet; typedef std::set<LfgLockStatus*> LfgLockStatusSet; typedef std::vector<LfgReward*> LfgRewardList; typedef std::map<uint32, LfgReward*> LfgRewardMap; +typedef std::vector<LfgProposal*> LfgProposalList; typedef std::map<uint32, LfgLockStatusSet*> LfgLockStatusMap; typedef std::map<uint64, LfgQueueInfo*> LfgQueueInfoMap; typedef std::map<uint32, LfgRoleCheck*> LfgRoleCheckMap; +typedef std::map<uint32, LfgProposal*> LfgProposalMap; typedef std::list<Player *> LfgPlayerList; class LFGMgr @@ -230,6 +294,9 @@ class LFGMgr void InitLFG(); void Join(Player *plr); void Leave(Player *plr, Group *grp = NULL); + void OfferContinue(Group *grp); + void TeleportPlayer(Player *plr, bool out); + void UpdateProposal(uint32 proposalId, uint32 lowGuid, uint8 accept); void UpdateRoleCheck(Group *grp, Player *plr = NULL); void Update(uint32 diff); @@ -237,6 +304,9 @@ class LFGMgr void SendLfgPartyInfo(Player *plr); private: + void SendUpdateProposal(Player *plr, uint32 proposalId, LfgProposal *pProp); + void SendLfgPlayerReward(Player *plr); + void BuildLfgRoleCheck(WorldPacket &data, LfgRoleCheck *pRoleCheck); void BuildAvailableRandomDungeonList(WorldPacket &data, Player *plr); void BuildPlayerLockDungeonBlock(WorldPacket &data, LfgLockStatusSet *lockSet); @@ -245,7 +315,9 @@ class LFGMgr void AddToQueue(uint64 guid, LfgRolesMap *roles, LfgDungeonSet *dungeons); bool RemoveFromQueue(uint64 guid); bool isRandomDungeon(uint32 dungeonId); + void FindNewGroups(LfgGuidList &check, LfgGuidList all, LfgProposalList *proposals); bool CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag = true); + void RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType type); LfgLockStatusMap* GetGroupLockStatusDungeons(PlayerSet *pPlayers, LfgDungeonSet *dungeons); LfgLockStatusMap* GetPartyLockStatusDungeons(Player *plr, LfgDungeonSet *dungeons); @@ -262,6 +334,7 @@ class LFGMgr LfgQueueInfoMap m_QueueInfoMap; // Queued groups LfgGuidList m_currentQueue; // Ordered list. Used to find groups LfgGuidList m_newToQueue; // New groups to add to queue; + LfgProposalMap m_Proposals; // Current Proposals LfgRoleCheckMap m_RoleChecks; // Current Role checks uint32 m_QueueTimer; // used to check interval of update uint32 m_lfgProposalId; // used as internal counter for proposals diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 5c2c8b84c1e..f05e465deee 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -49,7 +49,7 @@ Group::Group() m_counter = 0; m_maxEnchantingLevel= 0; m_LfgQueued = false; - m_LfgStatus = 1; + m_LfgStatus = LFG_STATUS_NOT_SAVED; m_LfgDungeonEntry = 0; for (uint8 i = 0; i < TARGETICONCOUNT; ++i) @@ -355,6 +355,8 @@ uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method) if (isLfgQueued()) sLFGMgr.Leave(NULL, this); + else if (isLFGGroup()) + sLFGMgr.OfferContinue(this); // remove member and change leader (if need) only if strong more 2 members _before_ member remove if (GetMembersCount() > (isBGGroup() ? 1 : 2)) // in BG group case allow 1 members group diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index 75ed94a46ff..78e766edc92 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -203,7 +203,8 @@ class Group return (m_LfgDungeonEntry & 0x00FFFFFF); else return m_LfgDungeonEntry; - } + } + void SetLfgRoles(uint64 guid, const uint8 roles) { member_witerator slot = _getMemberWSlot(guid); diff --git a/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp b/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp index 8c93e11eb20..8ea0fb17eb3 100644 --- a/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp @@ -384,6 +384,8 @@ void WorldSession::HandleGroupDisbandOpcode(WorldPacket & /*recv_data*/) SendPartyResult(PARTY_OP_LEAVE, GetPlayer()->GetName(), ERR_PARTY_RESULT_OK); GetPlayer()->RemoveFromGroup(); + if (grp->isLFGGroup() && GetPlayer()->GetMap()->IsDungeon()) + GetPlayer()->TeleportToBGEntryPoint(); } void WorldSession::HandleLootMethodOpcode(WorldPacket & recv_data) diff --git a/src/server/game/Server/Protocol/Handlers/LFGHandler.cpp b/src/server/game/Server/Protocol/Handlers/LFGHandler.cpp index 6c9a86b83ea..0e79e9a15ce 100644 --- a/src/server/game/Server/Protocol/Handlers/LFGHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/LFGHandler.cpp @@ -78,6 +78,9 @@ void WorldSession::HandleLfgProposalResultOpcode(WorldPacket &recv_data) uint8 accept; // Accept to join? recv_data >> lfgGroupID; recv_data >> accept; + + if (accept < 2) + sLFGMgr.UpdateProposal(lfgGroupID, GetPlayer()->GetGUIDLow(), accept); } void WorldSession::HandleLfgSetRolesOpcode(WorldPacket &recv_data) @@ -110,6 +113,7 @@ void WorldSession::HandleLfgSetBootVoteOpcode(WorldPacket &recv_data) uint8 agree; // Agree to kick player recv_data >> agree; + } void WorldSession::HandleLfgTeleportOpcode(WorldPacket &recv_data) @@ -118,6 +122,8 @@ void WorldSession::HandleLfgTeleportOpcode(WorldPacket &recv_data) bool out; recv_data >> out; + + sLFGMgr.TeleportPlayer(GetPlayer(), out); } void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket &/*recv_data*/) @@ -129,6 +135,7 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket &/*recv_data void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket &/*recv_data*/) { sLog.outDebug("CMSG_LFD_PARTY_LOCK_INFO_REQUEST"); + //sLFGMgr.SendLfgPartyInfo(GetPlayer()); } void WorldSession::HandleLfrSearchOpcode(WorldPacket &recv_data) @@ -137,6 +144,7 @@ void WorldSession::HandleLfrSearchOpcode(WorldPacket &recv_data) uint32 entry; // Raid id to search recv_data >> entry; + //SendLfrUpdateListOpcode(entry); } void WorldSession::HandleLfrLeaveOpcode(WorldPacket &recv_data) @@ -145,6 +153,7 @@ void WorldSession::HandleLfrLeaveOpcode(WorldPacket &recv_data) uint32 dungeonId; // Raid id queue to leave recv_data >> dungeonId; + //sLFGMgr.LeaveLfr(GetPlayer(), dungeonId); } void WorldSession::SendLfgUpdatePlayer(uint8 updateType) @@ -312,4 +321,14 @@ void WorldSession::SendLfgTeleportError(uint8 err) WorldPacket data(SMSG_LFG_TELEPORT_DENIED, 4); data << uint32(err); // Error SendPacket(&data); -}
\ No newline at end of file +} + +/* +void WorldSession::SendLfrUpdateListOpcode(uint32 entry) +{ + sLog.outDebug("SMSG_UPDATE_LFG_LIST"); + WorldPacket data(SMSG_UPDATE_LFG_LIST); + sLFGMgr.BuildLfrUpdateList(data, entry); + SendPacket(&data); +} +*/ |