aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpp <spp@jorge.gr>2010-08-11 15:55:27 +0200
committerSpp <spp@jorge.gr>2010-08-11 15:55:27 +0200
commit41534a13247aef321d555e19d08bf9a2b25a0fbd (patch)
tree3c1d2886a1fd047d47b7a77d1638e5e16e08e1c4
parentf9468e7e5d13071d170b4e10a4f20689e2ae7dc3 (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.cpp582
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.h73
-rw-r--r--src/server/game/Groups/Group.cpp4
-rw-r--r--src/server/game/Groups/Group.h3
-rw-r--r--src/server/game/Server/Protocol/Handlers/GroupHandler.cpp2
-rw-r--r--src/server/game/Server/Protocol/Handlers/LFGHandler.cpp21
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);
+}
+*/