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
This commit is contained in:
Spp
2010-08-11 15:55:27 +02:00
parent f9468e7e5d
commit 41534a1324
6 changed files with 682 additions and 3 deletions

View File

@@ -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())
@@ -341,6 +412,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>
@@ -397,6 +481,211 @@ bool LFGMgr::RemoveFromQueue(uint64 guid)
return false;
}
/// <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>
@@ -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
// --------------------------------------------------------------------------//
@@ -663,6 +1156,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>
@@ -732,6 +1276,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>

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);
}
}
/*
void WorldSession::SendLfrUpdateListOpcode(uint32 entry)
{
sLog.outDebug("SMSG_UPDATE_LFG_LIST");
WorldPacket data(SMSG_UPDATE_LFG_LIST);
sLFGMgr.BuildLfrUpdateList(data, entry);
SendPacket(&data);
}
*/