mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-23 05:36:23 +00:00
* revert (core): ChrRace.dbc full implementation we revert this due to several issues arrising. Although the dbc reading is done in full and correctly. Azerothcore relied on the original handling (althought not propper) for so long that there is * revert * Update remove_charrace_dbc.sql * Update remove_charrace_dbc.sql * Update remove_charrace_dbc.sql
2822 lines
111 KiB
C++
2822 lines
111 KiB
C++
/*
|
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Affero General Public License as published by the
|
|
* Free Software Foundation; either version 3 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "LFGMgr.h"
|
|
#include "BattlegroundMgr.h"
|
|
#include "CharacterCache.h"
|
|
#include "Common.h"
|
|
#include "DBCStores.h"
|
|
#include "DisableMgr.h"
|
|
#include "GameEventMgr.h"
|
|
#include "GameTime.h"
|
|
#include "Group.h"
|
|
#include "GroupMgr.h"
|
|
#include "InstanceSaveMgr.h"
|
|
#include "LFGGroupData.h"
|
|
#include "LFGPlayerData.h"
|
|
#include "LFGQueue.h"
|
|
#include "LFGScripts.h"
|
|
#include "Language.h"
|
|
#include "ObjectMgr.h"
|
|
#include "Opcodes.h"
|
|
#include "Player.h"
|
|
#include "SharedDefines.h"
|
|
#include "SocialMgr.h"
|
|
#include "SpellAuras.h"
|
|
#include "WorldSession.h"
|
|
|
|
namespace lfg
|
|
{
|
|
LFGMgr::LFGMgr(): m_lfgProposalId(1), m_options(sWorld->getIntConfig(CONFIG_LFG_OPTIONSMASK)), m_Testing(false)
|
|
{
|
|
new LFGPlayerScript();
|
|
new LFGGroupScript();
|
|
|
|
for (uint8 team = 0; team < 2; ++team)
|
|
{
|
|
m_raidBrowserUpdateTimer[team] = 10000;
|
|
m_raidBrowserLastUpdatedDungeonId[team] = 0;
|
|
}
|
|
}
|
|
|
|
LFGMgr::~LFGMgr()
|
|
{
|
|
for (LfgRewardContainer::iterator itr = RewardMapStore.begin(); itr != RewardMapStore.end(); ++itr)
|
|
delete itr->second;
|
|
}
|
|
|
|
LFGMgr* LFGMgr::instance()
|
|
{
|
|
static LFGMgr instance;
|
|
return &instance;
|
|
}
|
|
|
|
void LFGMgr::_LoadFromDB(Field* fields, ObjectGuid guid)
|
|
{
|
|
if (!fields)
|
|
return;
|
|
|
|
if (!guid.IsGroup())
|
|
return;
|
|
|
|
SetLeader(guid, ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>()));
|
|
|
|
uint32 dungeon = fields[17].Get<uint32>();
|
|
uint8 state = fields[18].Get<uint8>();
|
|
|
|
if (!dungeon || !state)
|
|
return;
|
|
|
|
SetDungeon(guid, dungeon);
|
|
|
|
switch (state)
|
|
{
|
|
case LFG_STATE_DUNGEON:
|
|
case LFG_STATE_FINISHED_DUNGEON:
|
|
SetState(guid, (LfgState)state);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void LFGMgr::_SaveToDB(ObjectGuid guid)
|
|
{
|
|
if (!guid.IsGroup())
|
|
return;
|
|
|
|
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_LFG_DATA);
|
|
stmt->SetData(0, guid.GetCounter());
|
|
stmt->SetData(1, GetDungeon(guid));
|
|
stmt->SetData(2, GetState(guid));
|
|
CharacterDatabase.Execute(stmt);
|
|
}
|
|
|
|
/// Load rewards for completing dungeons
|
|
void LFGMgr::LoadRewards()
|
|
{
|
|
uint32 oldMSTime = getMSTime();
|
|
|
|
for (LfgRewardContainer::iterator itr = RewardMapStore.begin(); itr != RewardMapStore.end(); ++itr)
|
|
delete itr->second;
|
|
RewardMapStore.clear();
|
|
|
|
// ORDER BY is very important for GetRandomDungeonReward!
|
|
QueryResult result = WorldDatabase.Query("SELECT dungeonId, maxLevel, firstQuestId, otherQuestId FROM lfg_dungeon_rewards ORDER BY dungeonId, maxLevel ASC");
|
|
|
|
if (!result)
|
|
{
|
|
LOG_ERROR("lfg", ">> Loaded 0 lfg dungeon rewards. DB table `lfg_dungeon_rewards` is empty!");
|
|
return;
|
|
}
|
|
|
|
uint32 count = 0;
|
|
|
|
Field* fields = nullptr;
|
|
do
|
|
{
|
|
fields = result->Fetch();
|
|
uint32 dungeonId = fields[0].Get<uint32>();
|
|
uint32 maxLevel = fields[1].Get<uint8>();
|
|
uint32 firstQuestId = fields[2].Get<uint32>();
|
|
uint32 otherQuestId = fields[3].Get<uint32>();
|
|
|
|
if (!GetLFGDungeonEntry(dungeonId))
|
|
{
|
|
LOG_ERROR("lfg", "Dungeon {} specified in table `lfg_dungeon_rewards` does not exist!", dungeonId);
|
|
continue;
|
|
}
|
|
|
|
if (!maxLevel || maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
|
{
|
|
LOG_ERROR("lfg", "Level {} specified for dungeon {} in table `lfg_dungeon_rewards` can never be reached!", maxLevel, dungeonId);
|
|
maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
|
}
|
|
|
|
if (!firstQuestId || !sObjectMgr->GetQuestTemplate(firstQuestId))
|
|
{
|
|
LOG_ERROR("lfg", "First quest {} specified for dungeon {} in table `lfg_dungeon_rewards` does not exist!", firstQuestId, dungeonId);
|
|
continue;
|
|
}
|
|
|
|
if (otherQuestId && !sObjectMgr->GetQuestTemplate(otherQuestId))
|
|
{
|
|
LOG_ERROR("lfg", "Other quest {} specified for dungeon {} in table `lfg_dungeon_rewards` does not exist!", otherQuestId, dungeonId);
|
|
otherQuestId = 0;
|
|
}
|
|
|
|
RewardMapStore.insert(LfgRewardContainer::value_type(dungeonId, new LfgReward(maxLevel, firstQuestId, otherQuestId)));
|
|
++count;
|
|
} while (result->NextRow());
|
|
|
|
LOG_INFO("server.loading", ">> Loaded {} LFG Dungeon Rewards in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
|
LOG_INFO("server.loading", " ");
|
|
}
|
|
|
|
LFGDungeonData const* LFGMgr::GetLFGDungeon(uint32 id)
|
|
{
|
|
LFGDungeonContainer::const_iterator itr = LfgDungeonStore.find(id);
|
|
if (itr != LfgDungeonStore.end())
|
|
return &(itr->second);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void LFGMgr::LoadLFGDungeons(bool reload /* = false */)
|
|
{
|
|
uint32 oldMSTime = getMSTime();
|
|
|
|
LfgDungeonStore.clear();
|
|
|
|
// Initialize Dungeon map with data from dbcs
|
|
for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i)
|
|
{
|
|
LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i);
|
|
if (!dungeon)
|
|
continue;
|
|
|
|
switch (dungeon->TypeID)
|
|
{
|
|
case LFG_TYPE_DUNGEON:
|
|
case LFG_TYPE_HEROIC:
|
|
case LFG_TYPE_RAID:
|
|
case LFG_TYPE_RANDOM:
|
|
LfgDungeonStore[dungeon->ID] = LFGDungeonData(dungeon);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Fill teleport locations from DB
|
|
// 0 1 2 3 4
|
|
QueryResult result = WorldDatabase.Query("SELECT dungeonId, position_x, position_y, position_z, orientation FROM lfg_dungeon_template");
|
|
|
|
if (!result)
|
|
{
|
|
LOG_ERROR("lfg", ">> Loaded 0 LFG Entrance Positions. DB Table `lfg_dungeon_template` Is Empty!");
|
|
LOG_INFO("server.loading", " ");
|
|
return;
|
|
}
|
|
|
|
uint32 count = 0;
|
|
|
|
do
|
|
{
|
|
Field* fields = result->Fetch();
|
|
uint32 dungeonId = fields[0].Get<uint32>();
|
|
LFGDungeonContainer::iterator dungeonItr = LfgDungeonStore.find(dungeonId);
|
|
if (dungeonItr == LfgDungeonStore.end())
|
|
{
|
|
LOG_ERROR("lfg", "table `lfg_dungeon_template` contains coordinates for wrong dungeon {}", dungeonId);
|
|
continue;
|
|
}
|
|
|
|
LFGDungeonData& data = dungeonItr->second;
|
|
data.x = fields[1].Get<float>();
|
|
data.y = fields[2].Get<float>();
|
|
data.z = fields[3].Get<float>();
|
|
data.o = fields[4].Get<float>();
|
|
|
|
++count;
|
|
} while (result->NextRow());
|
|
|
|
LOG_INFO("server.loading", ">> Loaded {} LFG Entrance Positions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
|
LOG_INFO("server.loading", " ");
|
|
|
|
// Fill all other teleport coords from areatriggers
|
|
for (LFGDungeonContainer::iterator itr = LfgDungeonStore.begin(); itr != LfgDungeonStore.end(); ++itr)
|
|
{
|
|
LFGDungeonData& dungeon = itr->second;
|
|
|
|
// No teleport coords in database, load from areatriggers
|
|
if (dungeon.type != LFG_TYPE_RANDOM && dungeon.x == 0.0f && dungeon.y == 0.0f && dungeon.z == 0.0f)
|
|
{
|
|
AreaTriggerTeleport const* at = sObjectMgr->GetMapEntranceTrigger(dungeon.map);
|
|
if (!at)
|
|
{
|
|
LOG_ERROR("lfg", "LFGMgr::LoadLFGDungeons: Failed to load dungeon {}, cant find areatrigger for map {}", dungeon.name, dungeon.map);
|
|
continue;
|
|
}
|
|
|
|
dungeon.map = at->target_mapId;
|
|
dungeon.x = at->target_X;
|
|
dungeon.y = at->target_Y;
|
|
dungeon.z = at->target_Z;
|
|
dungeon.o = at->target_Orientation;
|
|
}
|
|
|
|
if (dungeon.type != LFG_TYPE_RANDOM)
|
|
CachedDungeonMapStore[dungeon.group].insert(dungeon.id);
|
|
CachedDungeonMapStore[0].insert(dungeon.id);
|
|
}
|
|
|
|
if (reload)
|
|
{
|
|
CachedDungeonMapStore.clear();
|
|
// Recalculate locked dungeons
|
|
for (LfgPlayerDataContainer::const_iterator it = PlayersStore.begin(); it != PlayersStore.end(); ++it)
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(it->first))
|
|
InitializeLockedDungeons(player, nullptr);
|
|
}
|
|
}
|
|
|
|
void LFGMgr::Update(uint32 tdiff, uint8 task)
|
|
{
|
|
if (!isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER | LFG_OPTION_ENABLE_SEASONAL_BOSSES))
|
|
return;
|
|
|
|
if (task == 0)
|
|
{
|
|
time_t currTime = GameTime::GetGameTime().count();
|
|
|
|
// Remove obsolete role checks
|
|
for (LfgRoleCheckContainer::iterator it = RoleChecksStore.begin(); it != RoleChecksStore.end();)
|
|
{
|
|
LfgRoleCheckContainer::iterator itRoleCheck = it++;
|
|
LfgRoleCheck& roleCheck = itRoleCheck->second;
|
|
if (currTime < roleCheck.cancelTime)
|
|
continue;
|
|
roleCheck.state = LFG_ROLECHECK_MISSING_ROLE;
|
|
|
|
for (LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin(); itRoles != roleCheck.roles.end(); ++itRoles)
|
|
{
|
|
ObjectGuid guid = itRoles->first;
|
|
RestoreState(guid, "Remove Obsolete RoleCheck");
|
|
SendLfgRoleCheckUpdate(guid, roleCheck);
|
|
if (guid == roleCheck.leader)
|
|
SendLfgJoinResult(guid, LfgJoinResultData(LFG_JOIN_FAILED, LFG_ROLECHECK_MISSING_ROLE));
|
|
}
|
|
|
|
RestoreState(itRoleCheck->first, "Remove Obsolete RoleCheck");
|
|
RoleChecksStore.erase(itRoleCheck);
|
|
}
|
|
|
|
// Remove obsolete proposals
|
|
for (LfgProposalContainer::iterator it = ProposalsStore.begin(); it != ProposalsStore.end();)
|
|
{
|
|
LfgProposalContainer::iterator itRemove = it++;
|
|
if (itRemove->second.cancelTime < currTime)
|
|
RemoveProposal(itRemove, LFG_UPDATETYPE_PROPOSAL_FAILED);
|
|
}
|
|
|
|
// Remove obsolete kicks
|
|
for (LfgPlayerBootContainer::iterator it = BootsStore.begin(); it != BootsStore.end();)
|
|
{
|
|
LfgPlayerBootContainer::iterator itBoot = it++;
|
|
LfgPlayerBoot& boot = itBoot->second;
|
|
if (boot.cancelTime < currTime)
|
|
{
|
|
boot.inProgress = false;
|
|
for (LfgAnswerContainer::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes)
|
|
{
|
|
ObjectGuid pguid = itVotes->first;
|
|
if (pguid != boot.victim)
|
|
SendLfgBootProposalUpdate(pguid, boot);
|
|
SetState(pguid, LFG_STATE_DUNGEON);
|
|
}
|
|
SetState(itBoot->first, LFG_STATE_DUNGEON);
|
|
BootsStore.erase(itBoot);
|
|
}
|
|
}
|
|
}
|
|
else if (task == 1)
|
|
{
|
|
this->lastProposalId = m_lfgProposalId; // pussywizard: task 2 is done independantly, store previous value in LFGMgr for future use
|
|
uint8 newGroupsProcessed = 0;
|
|
// Check if a proposal can be formed with the new groups being added
|
|
for (LfgQueueContainer::iterator it = QueuesStore.begin(); it != QueuesStore.end(); ++it)
|
|
{
|
|
newGroupsProcessed += it->second.FindGroups();
|
|
if (newGroupsProcessed)
|
|
break;
|
|
}
|
|
|
|
// Update all players status queue info
|
|
if (!newGroupsProcessed) // don't do this on updates that precessed groups (performance)
|
|
for (LfgQueueContainer::iterator it = QueuesStore.begin(); it != QueuesStore.end(); ++it)
|
|
it->second.UpdateQueueTimers(tdiff);
|
|
}
|
|
else if (task == 2)
|
|
{
|
|
if (lastProposalId != m_lfgProposalId)
|
|
{
|
|
// pussywizard: only one proposal can be created in World::Update (during maps update), and it has id == m_lfgProposalId, so try to find only that one, dunno why for loop here xD
|
|
for (LfgProposalContainer::const_iterator itProposal = ProposalsStore.find(m_lfgProposalId); itProposal != ProposalsStore.end(); ++itProposal)
|
|
{
|
|
uint32 proposalId = itProposal->first;
|
|
LfgProposal& proposal = ProposalsStore[proposalId];
|
|
|
|
ObjectGuid guid;
|
|
for (LfgProposalPlayerContainer::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers)
|
|
{
|
|
guid = itPlayers->first;
|
|
SetState(guid, LFG_STATE_PROPOSAL);
|
|
if (ObjectGuid gguid = GetGroup(guid))
|
|
{
|
|
SetState(gguid, LFG_STATE_PROPOSAL);
|
|
SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid)));
|
|
}
|
|
else
|
|
SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid)));
|
|
SendLfgUpdateProposal(guid, proposal);
|
|
}
|
|
|
|
if (proposal.state == LFG_PROPOSAL_SUCCESS) // pussywizard: no idea what's the purpose of this xD
|
|
UpdateProposal(proposalId, guid, true);
|
|
}
|
|
}
|
|
|
|
UpdateRaidBrowser(tdiff);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Generate the dungeon lock map for a given player
|
|
|
|
@param[in] player Player we need to initialize the lock status map
|
|
*/
|
|
void LFGMgr::InitializeLockedDungeons(Player* player, Group const* group)
|
|
{
|
|
ObjectGuid guid = player->GetGUID();
|
|
|
|
uint8 level = player->GetLevel();
|
|
uint8 expansion = player->GetSession()->Expansion();
|
|
LfgDungeonSet const& dungeons = GetDungeonsByRandom(0);
|
|
LfgLockMap lock;
|
|
|
|
bool onlySeasonalBosses = m_options == LFG_OPTION_ENABLE_SEASONAL_BOSSES;
|
|
|
|
float avgItemLevel = player->GetAverageItemLevelForDF();
|
|
|
|
for (LfgDungeonSet::const_iterator it = dungeons.begin(); it != dungeons.end(); ++it)
|
|
{
|
|
LFGDungeonData const* dungeon = GetLFGDungeon(*it);
|
|
if (!dungeon) // should never happen - We provide a list from sLFGDungeonStore
|
|
continue;
|
|
MapEntry const* mapEntry = sMapStore.LookupEntry(dungeon->map);
|
|
DungeonProgressionRequirements const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty));
|
|
|
|
uint32 lockData = 0;
|
|
if (dungeon->expansion > expansion || (onlySeasonalBosses && !dungeon->seasonal))
|
|
lockData = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION;
|
|
else if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, dungeon->map, player))
|
|
lockData = LFG_LOCKSTATUS_RAID_LOCKED;
|
|
else if (DisableMgr::IsDisabledFor(DISABLE_TYPE_LFG_MAP, dungeon->map, player))
|
|
lockData = LFG_LOCKSTATUS_RAID_LOCKED;
|
|
else if (dungeon->difficulty > DUNGEON_DIFFICULTY_NORMAL && (!mapEntry || !mapEntry->IsRaid()) && sInstanceSaveMgr->PlayerIsPermBoundToInstance(player->GetGUID(), dungeon->map, Difficulty(dungeon->difficulty)))
|
|
lockData = LFG_LOCKSTATUS_RAID_LOCKED;
|
|
else if ((dungeon->minlevel > level && !sWorld->getBoolConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE)) || (sWorld->getBoolConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE) && ar && ar->levelMin > 0 && ar->levelMin > level))
|
|
lockData = LFG_LOCKSTATUS_TOO_LOW_LEVEL;
|
|
else if ((dungeon->maxlevel < level && !sWorld->getBoolConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE)) || (sWorld->getBoolConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE) && ar && ar->levelMax > 0 && ar->levelMax < level))
|
|
lockData = LFG_LOCKSTATUS_TOO_HIGH_LEVEL;
|
|
else if (dungeon->seasonal && !IsSeasonActive(dungeon->id))
|
|
lockData = LFG_LOCKSTATUS_NOT_IN_SEASON;
|
|
else if (ar)
|
|
{
|
|
// Check required items
|
|
for (const ProgressionRequirement* itemRequirement : ar->items)
|
|
{
|
|
if (!itemRequirement->checkLeaderOnly || !group || group->GetLeaderGUID() == player->GetGUID())
|
|
{
|
|
if (itemRequirement->faction == TEAM_NEUTRAL || itemRequirement->faction == player->GetTeamId(true))
|
|
{
|
|
if (!player->HasItemCount(itemRequirement->id, 1))
|
|
{
|
|
lockData = LFG_LOCKSTATUS_MISSING_ITEM;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check for quests
|
|
for (const ProgressionRequirement* questRequirement : ar->quests)
|
|
{
|
|
if (!questRequirement->checkLeaderOnly || !group || group->GetLeaderGUID() == player->GetGUID())
|
|
{
|
|
if (questRequirement->faction == TEAM_NEUTRAL || questRequirement->faction == player->GetTeamId(true))
|
|
{
|
|
if (!player->GetQuestRewardStatus(questRequirement->id))
|
|
{
|
|
lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check for ilvl
|
|
if (ar->reqItemLevel && (float)ar->reqItemLevel > avgItemLevel)
|
|
{
|
|
lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE;
|
|
}
|
|
|
|
//Check if player has the required achievements
|
|
for (const ProgressionRequirement* achievementRequirement : ar->achievements)
|
|
{
|
|
if (!achievementRequirement->checkLeaderOnly || !group || group->GetLeaderGUID() == player->GetGUID())
|
|
{
|
|
if (achievementRequirement->faction == TEAM_NEUTRAL || achievementRequirement->faction == player->GetTeamId(true))
|
|
{
|
|
if (!player->HasAchieved(achievementRequirement->id))
|
|
{
|
|
lockData = LFG_LOCKSTATUS_MISSING_ACHIEVEMENT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sScriptMgr->OnInitializeLockedDungeons(player, level, lockData, dungeon);
|
|
|
|
/* TODO VoA closed if WG is not under team control (LFG_LOCKSTATUS_RAID_LOCKED)
|
|
lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE;
|
|
lockData = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE;
|
|
lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL;
|
|
lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL;
|
|
*/
|
|
|
|
if (lockData)
|
|
lock[dungeon->Entry()] = lockData;
|
|
}
|
|
|
|
sScriptMgr->OnAfterInitializeLockedDungeons(player);
|
|
|
|
SetLockedDungeons(guid, lock);
|
|
}
|
|
|
|
/**
|
|
Adds the player/group to lfg queue. If player is in a group then it is the leader
|
|
of the group tying to join the group. Join conditions are checked before adding
|
|
to the new queue.
|
|
|
|
@param[in] player Player trying to join (or leader of group trying to join)
|
|
@param[in] roles Player selected roles
|
|
@param[in] dungeons Dungeons the player/group is applying for
|
|
@param[in] comment Player selected comment
|
|
*/
|
|
void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const std::string& comment)
|
|
{
|
|
if (!player || dungeons.empty())
|
|
return;
|
|
|
|
Group* grp = player->GetGroup();
|
|
ObjectGuid guid = player->GetGUID();
|
|
ObjectGuid gguid = grp ? grp->GetGUID() : guid;
|
|
LfgJoinResultData joinData;
|
|
LfgGuidSet players;
|
|
uint32 rDungeonId = 0;
|
|
bool isContinue = grp && grp->isLFGGroup() && GetState(gguid) != LFG_STATE_FINISHED_DUNGEON;
|
|
|
|
if (grp && (grp->isBGGroup() || grp->isBFGroup()))
|
|
return;
|
|
|
|
if (!sScriptMgr->CanJoinLfg(player, roles, dungeons, comment))
|
|
return;
|
|
|
|
// pussywizard: can't join LFG/LFR while using LFR
|
|
if (GetState(player->GetGUID()) == LFG_STATE_RAIDBROWSER)
|
|
{
|
|
LfgDungeonSet tmp;
|
|
SendRaidBrowserJoinedPacket(player, tmp, ""); // the df "eye" can disappear in various case, resend if needed
|
|
return;
|
|
}
|
|
|
|
// Do not allow to change dungeon in the middle of a current dungeon
|
|
if (isContinue)
|
|
{
|
|
dungeons.clear();
|
|
dungeons.insert(GetDungeon(gguid));
|
|
}
|
|
|
|
LfgState state = GetState(gguid);
|
|
switch (state)
|
|
{
|
|
case LFG_STATE_ROLECHECK: // if joining again during rolecheck (eg. many players clicked continue inside instance)
|
|
if (gguid.IsGroup())
|
|
UpdateRoleCheck(gguid); // abort role check and remove from RoleChecksStore
|
|
break;
|
|
case LFG_STATE_QUEUED: // joining again while in a queue
|
|
{
|
|
LFGQueue& queue = GetQueue(gguid);
|
|
queue.RemoveFromQueue(gguid);
|
|
}
|
|
break;
|
|
case LFG_STATE_PROPOSAL: // if joining again during proposal
|
|
joinData.result = LFG_JOIN_INTERNAL_ERROR;
|
|
break;
|
|
/*case LFG_STATE_FINISHED_DUNGEON:
|
|
if (grp && grp->isLFGGroup())
|
|
joinData.result = LFG_JOIN_PARTY_NOT_MEET_REQS;
|
|
break;
|
|
*/
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Check if all dungeons are valid
|
|
bool isRaid = false;
|
|
if (joinData.result == LFG_JOIN_OK)
|
|
{
|
|
bool isDungeon = false;
|
|
for (LfgDungeonSet::const_iterator it = dungeons.begin(); it != dungeons.end() && joinData.result == LFG_JOIN_OK; ++it)
|
|
{
|
|
LfgType type = GetDungeonType(*it);
|
|
switch (type)
|
|
{
|
|
case LFG_TYPE_RANDOM:
|
|
if (dungeons.size() > 1) // Only allow 1 random dungeon
|
|
{
|
|
joinData.result = LFG_JOIN_DUNGEON_INVALID;
|
|
}
|
|
else
|
|
{
|
|
rDungeonId = (*dungeons.begin());
|
|
sScriptMgr->OnPlayerQueueRandomDungeon(player, rDungeonId);
|
|
}
|
|
[[fallthrough]]; // On purpose (Random can only be dungeon or heroic dungeon)
|
|
case LFG_TYPE_HEROIC:
|
|
case LFG_TYPE_DUNGEON:
|
|
if (isRaid)
|
|
{
|
|
joinData.result = LFG_JOIN_MIXED_RAID_DUNGEON;
|
|
}
|
|
isDungeon = true;
|
|
break;
|
|
case LFG_TYPE_RAID:
|
|
if (isDungeon)
|
|
{
|
|
joinData.result = LFG_JOIN_MIXED_RAID_DUNGEON;
|
|
}
|
|
isRaid = true;
|
|
break;
|
|
default:
|
|
LOG_ERROR("lfg", "Wrong dungeon type {} for dungeon {}", type, *it);
|
|
joinData.result = LFG_JOIN_DUNGEON_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isRaid && joinData.result == LFG_JOIN_OK)
|
|
{
|
|
// Check player or group member restrictions
|
|
if (player->InBattleground() || (player->InBattlegroundQueue() && !sWorld->getBoolConfig(CONFIG_ALLOW_JOIN_BG_AND_LFG)))
|
|
{
|
|
joinData.result = LFG_JOIN_USING_BG_SYSTEM;
|
|
}
|
|
else if (player->HasAura(LFG_SPELL_DUNGEON_DESERTER))
|
|
{
|
|
joinData.result = LFG_JOIN_DESERTER;
|
|
}
|
|
else if (dungeons.empty())
|
|
{
|
|
joinData.result = LFG_JOIN_NOT_MEET_REQS;
|
|
}
|
|
else if (grp)
|
|
{
|
|
if (grp->GetMembersCount() > MAXGROUPSIZE)
|
|
joinData.result = LFG_JOIN_TOO_MUCH_MEMBERS;
|
|
else
|
|
{
|
|
uint8 memberCount = 0;
|
|
for (GroupReference* itr = grp->GetFirstMember(); itr != nullptr && joinData.result == LFG_JOIN_OK; itr = itr->next())
|
|
{
|
|
if (Player* plrg = itr->GetSource())
|
|
{
|
|
if (plrg->HasAura(LFG_SPELL_DUNGEON_DESERTER))
|
|
{
|
|
joinData.result = LFG_JOIN_PARTY_DESERTER;
|
|
}
|
|
else if (plrg->InBattleground() || (plrg->InBattlegroundQueue() && !sWorld->getBoolConfig(CONFIG_ALLOW_JOIN_BG_AND_LFG)))
|
|
{
|
|
joinData.result = LFG_JOIN_USING_BG_SYSTEM;
|
|
}
|
|
|
|
++memberCount;
|
|
players.insert(plrg->GetGUID());
|
|
}
|
|
}
|
|
|
|
if (joinData.result == LFG_JOIN_OK && memberCount != grp->GetMembersCount())
|
|
joinData.result = LFG_JOIN_DISCONNECTED;
|
|
}
|
|
}
|
|
else
|
|
players.insert(player->GetGUID());
|
|
|
|
// Xinef: Check dungeon cooldown only for random dungeons
|
|
// Xinef: Moreover check this only if dungeon is not started, afterwards its obvious that players will have the cooldown
|
|
if (joinData.result == LFG_JOIN_OK && !isContinue && rDungeonId)
|
|
{
|
|
if (player->HasAura(LFG_SPELL_DUNGEON_COOLDOWN)) // xinef: added !isContinue
|
|
joinData.result = LFG_JOIN_RANDOM_COOLDOWN;
|
|
else if (grp)
|
|
{
|
|
for (GroupReference* itr = grp->GetFirstMember(); itr != nullptr && joinData.result == LFG_JOIN_OK; itr = itr->next())
|
|
if (Player* plrg = itr->GetSource())
|
|
if (plrg->HasAura(LFG_SPELL_DUNGEON_COOLDOWN)) // xinef: added !isContinue
|
|
joinData.result = LFG_JOIN_PARTY_RANDOM_COOLDOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isRaid)
|
|
players.insert(player->GetGUID());
|
|
|
|
if (joinData.result == LFG_JOIN_OK)
|
|
{
|
|
// Expand random dungeons and check restrictions
|
|
if (rDungeonId)
|
|
dungeons = GetDungeonsByRandom(rDungeonId);
|
|
|
|
// if we have lockmap then there are no compatible dungeons
|
|
// xinef: dont check compatibile dungeons for already running group (bind problems)
|
|
if (!isContinue)
|
|
{
|
|
GetCompatibleDungeons(dungeons, players, joinData.lockmap);
|
|
if (dungeons.empty())
|
|
joinData.result = grp ? LFG_JOIN_PARTY_NOT_MEET_REQS : LFG_JOIN_NOT_MEET_REQS;
|
|
}
|
|
}
|
|
|
|
// pussywizard:
|
|
if (isRaid && grp && (grp->isLFGGroup() || guid != grp->GetLeaderGUID()))
|
|
return;
|
|
|
|
// Do not allow to change dungeon in the middle of a current dungeon
|
|
if (!isRaid && isContinue && grp->GetMembersCount() == 5)
|
|
{
|
|
dungeons.clear();
|
|
dungeons.insert(GetDungeon(gguid));
|
|
joinData.result = LFG_JOIN_PARTY_NOT_MEET_REQS;
|
|
}
|
|
|
|
// Can't join. Send result
|
|
if (joinData.result != LFG_JOIN_OK)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::Join: [{}] joining with {} members. result: {}", guid.ToString(), grp ? grp->GetMembersCount() : 1, joinData.result);
|
|
if (!dungeons.empty()) // Only should show lockmap when have no dungeons available
|
|
joinData.lockmap.clear();
|
|
player->GetSession()->SendLfgJoinResult(joinData);
|
|
return;
|
|
}
|
|
|
|
SetComment(guid, comment);
|
|
|
|
if (isRaid)
|
|
{
|
|
if (grp)
|
|
roles = PLAYER_ROLE_LEADER;
|
|
else
|
|
roles &= (PLAYER_ROLE_TANK | PLAYER_ROLE_HEALER | PLAYER_ROLE_DAMAGE);
|
|
if (!roles)
|
|
return;
|
|
JoinRaidBrowser(player, roles, dungeons, comment);
|
|
SetState(guid, LFG_STATE_RAIDBROWSER);
|
|
SendRaidBrowserJoinedPacket(player, dungeons, comment);
|
|
return;
|
|
}
|
|
|
|
std::string debugNames = "";
|
|
if (grp) // Begin rolecheck
|
|
{
|
|
// Create new rolecheck
|
|
LfgRoleCheck& roleCheck = RoleChecksStore[gguid];
|
|
roleCheck.roles.clear(); // pussywizard: NEW rolecheck, not old one with trash data >_>
|
|
roleCheck.cancelTime = time_t(GameTime::GetGameTime().count()) + LFG_TIME_ROLECHECK;
|
|
roleCheck.state = LFG_ROLECHECK_INITIALITING;
|
|
roleCheck.leader = guid;
|
|
roleCheck.dungeons = dungeons;
|
|
roleCheck.rDungeonId = rDungeonId;
|
|
|
|
if (rDungeonId)
|
|
{
|
|
dungeons.clear();
|
|
dungeons.insert(rDungeonId);
|
|
}
|
|
|
|
SetState(gguid, LFG_STATE_ROLECHECK);
|
|
// Send update to player
|
|
LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_JOIN_QUEUE, dungeons, comment);
|
|
for (GroupReference* itr = grp->GetFirstMember(); itr != nullptr; itr = itr->next())
|
|
{
|
|
if (Player* plrg = itr->GetSource())
|
|
{
|
|
ObjectGuid pguid = plrg->GetGUID();
|
|
plrg->GetSession()->SendLfgUpdateParty(updateData);
|
|
SetState(pguid, LFG_STATE_ROLECHECK);
|
|
if (!isContinue)
|
|
SetSelectedDungeons(pguid, dungeons);
|
|
roleCheck.roles[pguid] = 0;
|
|
if (!debugNames.empty())
|
|
debugNames.append(", ");
|
|
debugNames.append(plrg->GetName());
|
|
}
|
|
}
|
|
// Update leader role
|
|
UpdateRoleCheck(gguid, guid, roles);
|
|
}
|
|
else // Add player to queue
|
|
{
|
|
LfgRolesMap rolesMap;
|
|
rolesMap[guid] = roles;
|
|
LFGQueue& queue = GetQueue(guid);
|
|
queue.AddQueueData(guid, GameTime::GetGameTime().count(), dungeons, rolesMap);
|
|
|
|
if (!isContinue)
|
|
{
|
|
if (rDungeonId)
|
|
{
|
|
dungeons.clear();
|
|
dungeons.insert(rDungeonId);
|
|
}
|
|
SetSelectedDungeons(guid, dungeons);
|
|
}
|
|
// Send update to player
|
|
player->GetSession()->SendLfgJoinResult(joinData);
|
|
player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_JOIN_QUEUE, dungeons, comment));
|
|
SetState(guid, LFG_STATE_QUEUED);
|
|
SetRoles(guid, roles);
|
|
debugNames.append(player->GetName());
|
|
}
|
|
|
|
/*if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG))
|
|
{
|
|
std::ostringstream o;
|
|
o << "LFGMgr::Join: [" << guid << "] joined (" << (grp ? "group" : "player") << ") Members: " << debugNames.c_str()
|
|
<< ". Dungeons (" << uint32(dungeons.size()) << "): " << ConcatenateDungeons(dungeons);
|
|
LOG_DEBUG("lfg", "{}", o.str());
|
|
}*/
|
|
}
|
|
|
|
void LFGMgr::ToggleTesting()
|
|
{
|
|
m_Testing = !m_Testing;
|
|
sWorld->SendWorldText(m_Testing ? LANG_DEBUG_LFG_ON : LANG_DEBUG_LFG_OFF);
|
|
}
|
|
|
|
/**
|
|
Leaves Dungeon System. Player/Group is removed from queue, rolechecks, proposals
|
|
or votekicks. Player or group needs to be not nullptr and using Dungeon System
|
|
|
|
@param[in] guid Player or group guid
|
|
*/
|
|
void LFGMgr::LeaveLfg(ObjectGuid guid)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::Leave: [{}]", guid.ToString());
|
|
ObjectGuid gguid = guid.IsGroup() ? guid : GetGroup(guid);
|
|
LfgState state = GetState(guid);
|
|
switch (state)
|
|
{
|
|
case LFG_STATE_QUEUED:
|
|
if (gguid)
|
|
{
|
|
LFGQueue& queue = GetQueue(gguid);
|
|
queue.RemoveFromQueue(gguid);
|
|
uint32 dungeonId = GetDungeon(gguid);
|
|
SetState(gguid, LFG_STATE_NONE);
|
|
const LfgGuidSet& players = GetPlayers(gguid);
|
|
for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
|
|
{
|
|
SetState(*it, LFG_STATE_NONE);
|
|
SendLfgUpdateParty(*it, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
|
|
}
|
|
if (Group* group = sGroupMgr->GetGroupByGUID(gguid.GetCounter()))
|
|
{
|
|
if (group->isLFGGroup())
|
|
{
|
|
SetDungeon(gguid, dungeonId);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LFGQueue& queue = GetQueue(guid);
|
|
queue.RemoveFromQueue(guid);
|
|
SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
|
|
SetState(guid, LFG_STATE_NONE);
|
|
}
|
|
break;
|
|
case LFG_STATE_ROLECHECK:
|
|
if (gguid)
|
|
UpdateRoleCheck(gguid); // No player to update role = LFG_ROLECHECK_ABORTED
|
|
break;
|
|
case LFG_STATE_PROPOSAL:
|
|
{
|
|
// Remove from Proposals
|
|
LfgProposalContainer::iterator it = ProposalsStore.begin();
|
|
ObjectGuid pguid = gguid == guid ? GetLeader(gguid) : guid;
|
|
while (it != ProposalsStore.end())
|
|
{
|
|
LfgProposalPlayerContainer::iterator itPlayer = it->second.players.find(pguid);
|
|
if (itPlayer != it->second.players.end())
|
|
{
|
|
// Mark the player/leader of group who left as didn't accept the proposal
|
|
itPlayer->second.accept = LFG_ANSWER_DENY;
|
|
break;
|
|
}
|
|
++it;
|
|
}
|
|
|
|
// Remove from queue - if proposal is found, RemoveProposal will call RemoveFromQueue
|
|
if (it != ProposalsStore.end())
|
|
RemoveProposal(it, LFG_UPDATETYPE_PROPOSAL_DECLINED);
|
|
break;
|
|
}
|
|
case LFG_STATE_NONE:
|
|
break;
|
|
case LFG_STATE_DUNGEON:
|
|
case LFG_STATE_FINISHED_DUNGEON:
|
|
case LFG_STATE_BOOT:
|
|
if (guid != gguid) // Player
|
|
SetState(guid, LFG_STATE_NONE);
|
|
break;
|
|
case LFG_STATE_RAIDBROWSER:
|
|
LeaveRaidBrowser(guid);
|
|
SetCanOverrideRBState(guid, true);
|
|
SetState(guid, LFG_STATE_NONE);
|
|
SetCanOverrideRBState(guid, false);
|
|
SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_LEAVE_RAIDBROWSER));
|
|
SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_LEAVE_RAIDBROWSER));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void LFGMgr::JoinRaidBrowser(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string comment)
|
|
{
|
|
// pussywizard: client limit for comment length is 64 @ 3.3.5a
|
|
if (comment.size() > 64)
|
|
comment = comment.substr(0, 64);
|
|
|
|
RBEntryInfo entry(roles, comment);
|
|
for (LfgDungeonSet::const_iterator itr = dungeons.begin(); itr != dungeons.end(); ++itr)
|
|
if (GetLFGDungeon(*itr)) // ensure dungeon data exists for such dungeon id
|
|
{
|
|
RaidBrowserStore[player->GetTeamId()][*itr][player->GetGUID()] = entry;
|
|
RBUsedDungeonsStore[player->GetTeamId()].insert(*itr);
|
|
}
|
|
}
|
|
|
|
void LFGMgr::LeaveRaidBrowser(ObjectGuid guid)
|
|
{
|
|
for (uint8 team = 0; team < 2; ++team)
|
|
for (RBStoreMap::iterator itr = RaidBrowserStore[team].begin(); itr != RaidBrowserStore[team].end(); ++itr)
|
|
itr->second.erase(guid);
|
|
}
|
|
|
|
void LFGMgr::SendRaidBrowserJoinedPacket(Player* p, LfgDungeonSet& dungeons, std::string comment)
|
|
{
|
|
if (dungeons.empty())
|
|
{
|
|
RBEntryInfoMap::iterator iter;
|
|
uint8 team = p->GetTeamId();
|
|
bool setComment = true;
|
|
for (RBStoreMap::iterator itr = RaidBrowserStore[team].begin(); itr != RaidBrowserStore[team].end(); ++itr)
|
|
if ((iter = itr->second.find(p->GetGUID())) != itr->second.end())
|
|
{
|
|
dungeons.insert(itr->first);
|
|
if (setComment)
|
|
{
|
|
comment = iter->second.comment;
|
|
setComment = false;
|
|
}
|
|
}
|
|
}
|
|
LfgJoinResultData joinData;
|
|
p->GetSession()->SendLfgJoinResult(joinData);
|
|
LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_JOIN_RAIDBROWSER, dungeons, comment);
|
|
if (p->GetGroup())
|
|
p->GetSession()->SendLfgUpdateParty(updateData);
|
|
else
|
|
p->GetSession()->SendLfgUpdatePlayer(updateData);
|
|
}
|
|
|
|
void LFGMgr::LfrSearchAdd(Player* p, uint32 dungeonId)
|
|
{
|
|
RBSearchersStore[p->GetTeamId()][p->GetGUID()] = dungeonId;
|
|
}
|
|
|
|
void LFGMgr::LfrSearchRemove(Player* p)
|
|
{
|
|
RBSearchersStore[p->GetTeamId()].erase(p->GetGUID());
|
|
}
|
|
|
|
void LFGMgr::SendRaidBrowserCachedList(Player* player, uint32 dungeonId)
|
|
{
|
|
RBCacheMap::iterator itr = RBCacheStore[player->GetTeamId()].find(dungeonId);
|
|
if (itr != RBCacheStore[player->GetTeamId()].end())
|
|
{
|
|
player->GetSession()->SendPacket(&(itr->second));
|
|
return;
|
|
}
|
|
// send empty packet if cache not found
|
|
WorldPacket data(SMSG_UPDATE_LFG_LIST, 1000);
|
|
data << (uint32)LFG_TYPE_RAID;
|
|
data << (uint32)dungeonId;
|
|
data << (uint8)0;
|
|
data << (uint32)0;
|
|
data << (uint32)0;
|
|
data << (uint32)0;
|
|
data << (uint32)0;
|
|
player->GetSession()->SendPacket(&data);
|
|
}
|
|
|
|
void LFGMgr::UpdateRaidBrowser(uint32 diff)
|
|
{
|
|
for (uint8 team = 0; team < 2; ++team)
|
|
{
|
|
if (m_raidBrowserUpdateTimer[team] > diff)
|
|
m_raidBrowserUpdateTimer[team] -= diff;
|
|
else
|
|
m_raidBrowserUpdateTimer[team] = 0;
|
|
}
|
|
|
|
if (GetMSTimeDiff(GameTime::GetGameTimeMS(), GetTimeMS()) > 98ms) // prevent lagging
|
|
{
|
|
return;
|
|
}
|
|
|
|
ObjectGuid guid, groupGuid, instanceGuid;
|
|
uint8 level, Class, race, talents[3];
|
|
float iLevel, mp5, mp5combat, baseAP, rangedAP;
|
|
int32 spellDamage, spellHeal;
|
|
uint32 dungeonId, encounterMask, maxPower;
|
|
uint32 deletedCounter, groupCounter, playerCounter;
|
|
ByteBuffer buffer_deleted, buffer_groups, buffer_players;
|
|
std::string emptyComment;
|
|
GuidSet deletedGroups, deletedGroupsToErase;
|
|
RBInternalInfoMap copy;
|
|
|
|
for (uint8 team = 0; team < 2; ++team)
|
|
{
|
|
if (m_raidBrowserLastUpdatedDungeonId[team] == 0) // new loop
|
|
{
|
|
if (m_raidBrowserUpdateTimer[team] > 0) // allowed only with some time interval
|
|
continue;
|
|
else // reset timer
|
|
m_raidBrowserUpdateTimer[team] = 5000;
|
|
}
|
|
|
|
RBUsedDungeonsSet::const_iterator neitr, titr;
|
|
for (neitr = RBUsedDungeonsStore[team].begin(); neitr != RBUsedDungeonsStore[team].end(); )
|
|
{
|
|
titr = neitr++;
|
|
dungeonId = (*titr);
|
|
|
|
// go to next dungeon than previously (one dungeon updated in one LFGMgr::UpdateRaidBrowser)
|
|
if (dungeonId <= m_raidBrowserLastUpdatedDungeonId[team])
|
|
continue;
|
|
m_raidBrowserLastUpdatedDungeonId[team] = dungeonId;
|
|
|
|
RBEntryInfoMap& entryInfoMap = RaidBrowserStore[team][dungeonId];
|
|
LFGDungeonData const* dungeonData = GetLFGDungeon(dungeonId); // checked if exists before inserting to the container
|
|
RBInternalInfoMap& currInternalInfoMap = RBInternalInfoStoreCurr[team][dungeonId];
|
|
for (RBEntryInfoMap::const_iterator sitr = entryInfoMap.begin(); sitr != entryInfoMap.end(); ++sitr)
|
|
{
|
|
guid = sitr->first;
|
|
groupGuid.Clear();
|
|
Player* p = ObjectAccessor::FindConnectedPlayer(guid);
|
|
ASSERT(p);
|
|
if (sitr->second.roles == PLAYER_ROLE_LEADER)
|
|
{
|
|
ASSERT(p->GetGroup());
|
|
groupGuid = p->GetGroup()->GetGUID();
|
|
}
|
|
encounterMask = 0;
|
|
instanceGuid.Clear();
|
|
if (InstancePlayerBind* bind = sInstanceSaveMgr->PlayerGetBoundInstance(guid, dungeonData->map, dungeonData->difficulty))
|
|
if (bind->perm)
|
|
{
|
|
instanceGuid = ObjectGuid::Create<HighGuid::Instance>(bind->save->GetInstanceId());
|
|
encounterMask = bind->save->GetCompletedEncounterMask();
|
|
}
|
|
|
|
talents[0] = 0;
|
|
talents[1] = 0;
|
|
talents[2] = 0;
|
|
p->GetTalentTreePoints(talents);
|
|
spellDamage = p->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_ALL);
|
|
spellHeal = p->SpellBaseHealingBonusDone(SPELL_SCHOOL_MASK_ALL);
|
|
mp5 = p->GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER);
|
|
mp5combat = p->GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER);
|
|
baseAP = p->GetTotalAttackPowerValue(BASE_ATTACK);
|
|
rangedAP = p->GetTotalAttackPowerValue(RANGED_ATTACK);
|
|
maxPower = 0;
|
|
if (p->getClass() == CLASS_DRUID)
|
|
maxPower = p->GetMaxPower(POWER_MANA);
|
|
else
|
|
maxPower = (p->getPowerType() == POWER_RAGE || p->getPowerType() == POWER_RUNIC_POWER) ? p->GetMaxPower(p->getPowerType()) / 10 : p->GetMaxPower(p->getPowerType());
|
|
|
|
currInternalInfoMap[sitr->first] = RBInternalInfo(guid, sitr->second.comment, !groupGuid.IsEmpty(), groupGuid, sitr->second.roles, encounterMask, instanceGuid,
|
|
1, p->GetLevel(), p->getClass(), p->getRace(), p->GetAverageItemLevel(),
|
|
talents, p->GetAreaId(), p->GetArmor(), (uint32)std::max<int32>(0, spellDamage), (uint32)std::max<int32>(0, spellHeal),
|
|
p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_CRIT_MELEE)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_CRIT_RANGED)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_CRIT_SPELL)), std::max<float>(0.0f, mp5), std::max<float>(0.0f, mp5combat),
|
|
std::max<uint32>(baseAP, rangedAP), (uint32)p->GetStat(STAT_AGILITY), p->GetMaxHealth(), maxPower, p->GetDefenseSkillValue(),
|
|
p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_DODGE)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_BLOCK)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_PARRY)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_HASTE_SPELL)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_EXPERTISE)));
|
|
|
|
if (!groupGuid)
|
|
continue;
|
|
for (Group::member_citerator mitr = p->GetGroup()->GetMemberSlots().begin(); mitr != p->GetGroup()->GetMemberSlots().end(); ++mitr)
|
|
{
|
|
if (mitr->guid == sitr->first) // leader already added
|
|
continue;
|
|
|
|
guid = mitr->guid;
|
|
level = 1;
|
|
Class = 0;
|
|
race = 0;
|
|
iLevel = 0.0f;
|
|
talents[0] = 0;
|
|
talents[1] = 0;
|
|
talents[2] = 0;
|
|
if (CharacterCacheEntry const* gpd = sCharacterCache->GetCharacterCacheByGuid(mitr->guid))
|
|
{
|
|
level = gpd->Level;
|
|
Class = gpd->Class;
|
|
race = gpd->Race;
|
|
}
|
|
Player* mplr = ObjectAccessor::FindConnectedPlayer(guid);
|
|
if (mplr)
|
|
{
|
|
iLevel = mplr->GetAverageItemLevel();
|
|
mplr->GetTalentTreePoints(talents);
|
|
}
|
|
currInternalInfoMap[mitr->guid] = RBInternalInfo(guid, emptyComment, false, groupGuid, 0, 0, ObjectGuid::Empty,
|
|
(mplr ? 1 : 0), level, Class, race, iLevel,
|
|
talents, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
copy.clear();
|
|
copy = currInternalInfoMap; // will be saved as prev at the end
|
|
|
|
// compare prev with curr to build difference packet
|
|
deletedCounter = 0;
|
|
groupCounter = 0;
|
|
playerCounter = 0;
|
|
buffer_deleted.clear();
|
|
buffer_groups.clear();
|
|
buffer_players.clear();
|
|
deletedGroups.clear();
|
|
deletedGroupsToErase.clear();
|
|
|
|
RBInternalInfoMap& prevInternalInfoMap = RBInternalInfoStorePrev[team][dungeonId];
|
|
RBInternalInfoMap::iterator iter, iterTmp;
|
|
for (RBInternalInfoMap::const_iterator sitr = prevInternalInfoMap.begin(); sitr != prevInternalInfoMap.end(); ++sitr)
|
|
{
|
|
iter = currInternalInfoMap.find(sitr->first);
|
|
if (iter == currInternalInfoMap.end()) // was -> isn't
|
|
{
|
|
if (sitr->second.isGroupLeader)
|
|
deletedGroups.insert(sitr->second.groupGuid);
|
|
++deletedCounter;
|
|
buffer_deleted << sitr->second.guid;
|
|
}
|
|
else // was -> is
|
|
{
|
|
if (sitr->second.isGroupLeader) // was a leader
|
|
{
|
|
if (!iter->second.isGroupLeader) // leader -> no longer a leader
|
|
deletedGroups.insert(sitr->second.groupGuid);
|
|
else if (sitr->second.groupGuid != iter->second.groupGuid) // leader -> leader of another group
|
|
{
|
|
deletedGroups.insert(sitr->second.groupGuid);
|
|
deletedGroupsToErase.insert(iter->second.groupGuid);
|
|
++groupCounter;
|
|
RBPacketAppendGroup(iter->second, buffer_groups);
|
|
}
|
|
else if (sitr->second.comment != iter->second.comment || sitr->second.encounterMask != iter->second.encounterMask || sitr->second.instanceGuid != iter->second.instanceGuid) // leader -> nothing changed
|
|
{
|
|
++groupCounter;
|
|
RBPacketAppendGroup(iter->second, buffer_groups);
|
|
}
|
|
}
|
|
else if (iter->second.isGroupLeader) // wasn't a leader -> is a leader
|
|
{
|
|
deletedGroupsToErase.insert(iter->second.groupGuid);
|
|
++groupCounter;
|
|
RBPacketAppendGroup(iter->second, buffer_groups);
|
|
}
|
|
|
|
if (!iter->second._online) // if offline, copy previous stats (itemLevel, talents, area, etc.)
|
|
{
|
|
iterTmp = copy.find(sitr->first); // copied container is for building a full packet, so modify it there (currInternalInfoMap is erased)
|
|
iterTmp->second.CopyStats(sitr->second);
|
|
if (!sitr->second.PlayerSameAs(iterTmp->second)) // player info changed
|
|
{
|
|
++playerCounter;
|
|
RBPacketAppendPlayer(iterTmp->second, buffer_players);
|
|
}
|
|
}
|
|
else if (!sitr->second.PlayerSameAs(iter->second)) // player info changed
|
|
{
|
|
++playerCounter;
|
|
RBPacketAppendPlayer(iter->second, buffer_players);
|
|
}
|
|
currInternalInfoMap.erase(iter);
|
|
}
|
|
}
|
|
// left entries (new)
|
|
for (RBInternalInfoMap::const_iterator sitr = currInternalInfoMap.begin(); sitr != currInternalInfoMap.end(); ++sitr)
|
|
{
|
|
if (sitr->second.isGroupLeader)
|
|
{
|
|
deletedGroupsToErase.insert(sitr->second.groupGuid);
|
|
++groupCounter;
|
|
RBPacketAppendGroup(sitr->second, buffer_groups);
|
|
}
|
|
++playerCounter;
|
|
RBPacketAppendPlayer(sitr->second, buffer_players);
|
|
}
|
|
|
|
if (!deletedGroupsToErase.empty())
|
|
{
|
|
for (ObjectGuid const& toErase : deletedGroupsToErase)
|
|
{
|
|
deletedGroups.erase(toErase);
|
|
}
|
|
}
|
|
|
|
if (!deletedGroups.empty())
|
|
{
|
|
for (ObjectGuid const& deletedGroup : deletedGroups)
|
|
{
|
|
++deletedCounter;
|
|
buffer_deleted << deletedGroup;
|
|
}
|
|
}
|
|
|
|
WorldPacket differencePacket(SMSG_UPDATE_LFG_LIST, 1000);
|
|
RBPacketBuildDifference(differencePacket, dungeonId, deletedCounter, buffer_deleted, groupCounter, buffer_groups, playerCounter, buffer_players);
|
|
WorldPacket fullPacket(SMSG_UPDATE_LFG_LIST, 1000);
|
|
RBPacketBuildFull(fullPacket, dungeonId, copy);
|
|
|
|
RBCacheStore[team][dungeonId] = fullPacket;
|
|
prevInternalInfoMap = copy;
|
|
currInternalInfoMap.clear();
|
|
|
|
if (entryInfoMap.empty())
|
|
RBUsedDungeonsStore[team].erase(titr);
|
|
|
|
// send difference packet to browsing players
|
|
for (RBSearchersMap::const_iterator sitr = RBSearchersStore[team].begin(); sitr != RBSearchersStore[team].end(); ++sitr)
|
|
if (sitr->second == dungeonId)
|
|
if (Player* p = ObjectAccessor::FindConnectedPlayer(sitr->first))
|
|
p->GetSession()->SendPacket(&differencePacket);
|
|
|
|
break; // one dungeon updated in one LFGMgr::UpdateRaidBrowser
|
|
}
|
|
|
|
// already updated all in this time interval
|
|
if (neitr == RBUsedDungeonsStore[team].end())
|
|
m_raidBrowserLastUpdatedDungeonId[team] = 0;
|
|
}
|
|
}
|
|
|
|
void LFGMgr::RBPacketAppendGroup(const RBInternalInfo& info, ByteBuffer& buffer)
|
|
{
|
|
buffer << info.groupGuid;
|
|
uint32 flags = LFG_UPDATE_FLAG_COMMENT | LFG_UPDATE_FLAG_ROLES | LFG_UPDATE_FLAG_BINDED;
|
|
buffer << (uint32)flags;
|
|
if (flags & LFG_UPDATE_FLAG_COMMENT)
|
|
buffer << info.comment;
|
|
if (flags & LFG_UPDATE_FLAG_ROLES)
|
|
for (uint8 j = 0; j < 3; ++j)
|
|
buffer << (uint8)0;
|
|
if (!(flags & LFG_UPDATE_FLAG_BINDED))
|
|
return;
|
|
buffer << info.instanceGuid;
|
|
buffer << (uint32)info.encounterMask;
|
|
}
|
|
|
|
void LFGMgr::RBPacketAppendPlayer(const RBInternalInfo& info, ByteBuffer& buffer)
|
|
{
|
|
buffer << info.guid;
|
|
uint32 flags = LFG_UPDATE_FLAG_CHARACTERINFO | LFG_UPDATE_FLAG_ROLES | LFG_UPDATE_FLAG_COMMENT | (info.groupGuid ? LFG_UPDATE_FLAG_GROUPGUID : LFG_UPDATE_FLAG_BINDED) | (info.isGroupLeader ? LFG_UPDATE_FLAG_GROUPLEADER : 0) | (!info.groupGuid || info.isGroupLeader ? LFG_UPDATE_FLAG_AREA : 0);
|
|
buffer << (uint32)flags;
|
|
|
|
if (flags & LFG_UPDATE_FLAG_CHARACTERINFO)
|
|
{
|
|
buffer << (uint8)info._level;
|
|
buffer << (uint8)info._class;
|
|
buffer << (uint8)info._race;
|
|
buffer << (uint8)info._talents0;
|
|
buffer << (uint8)info._talents1;
|
|
buffer << (uint8)info._talents2;
|
|
buffer << (uint32)info._armor;
|
|
buffer << (uint32)info._spellDamage;
|
|
buffer << (uint32)info._spellHeal;
|
|
buffer << (uint32)info._critRatingMelee;
|
|
buffer << (uint32)info._critRatingRanged;
|
|
buffer << (uint32)info._critRatingSpell;
|
|
buffer << (float)info._mp5;
|
|
buffer << (float)info._mp5combat;
|
|
buffer << (uint32)info._attackPower;
|
|
buffer << (uint32)info._agility;
|
|
buffer << (uint32)info._health;
|
|
buffer << (uint32)info._mana;
|
|
buffer << (uint32)info._online; // talentpoints, used as online/offline marker :D
|
|
buffer << (float)info._avgItemLevel; // avgitemlevel
|
|
buffer << (uint32)info._defenseSkill;
|
|
buffer << (uint32)info._dodgeRating;
|
|
buffer << (uint32)info._blockRating;
|
|
buffer << (uint32)info._parryRating;
|
|
buffer << (uint32)info._hasteRating;
|
|
buffer << (uint32)info._expertiseRating;
|
|
}
|
|
|
|
if (flags & LFG_UPDATE_FLAG_COMMENT)
|
|
buffer << (info.groupGuid ? std::string("") : info.comment);
|
|
if (flags & LFG_UPDATE_FLAG_GROUPLEADER)
|
|
buffer << (uint8)1; // isLFM
|
|
if (flags & LFG_UPDATE_FLAG_GROUPGUID)
|
|
buffer << info.groupGuid;
|
|
if (flags & LFG_UPDATE_FLAG_ROLES)
|
|
buffer << (uint8)(info.groupGuid ? (info.isGroupLeader ? PLAYER_ROLE_LEADER : 0) : info.roles);
|
|
if (flags & LFG_UPDATE_FLAG_AREA)
|
|
buffer << (uint32)info._area;
|
|
if (flags & LFG_UPDATE_FLAG_STATUS)
|
|
buffer << (uint8)0;
|
|
if (!(flags & LFG_UPDATE_FLAG_BINDED))
|
|
return;
|
|
buffer << info.instanceGuid;
|
|
buffer << (uint32)info.encounterMask;
|
|
}
|
|
|
|
void LFGMgr::RBPacketBuildDifference(WorldPacket& differencePacket, uint32 dungeonId, uint32 deletedCounter, ByteBuffer& buffer_deleted, uint32 groupCounter, ByteBuffer& buffer_groups, uint32 playerCounter, ByteBuffer& buffer_players)
|
|
{
|
|
differencePacket << (uint32)LFG_TYPE_RAID;
|
|
differencePacket << (uint32)dungeonId;
|
|
differencePacket << (uint8)1;
|
|
differencePacket << (uint32)deletedCounter;
|
|
differencePacket.append(buffer_deleted);
|
|
differencePacket << (uint32)groupCounter;
|
|
differencePacket << (uint32)0;
|
|
differencePacket.append(buffer_groups);
|
|
differencePacket << (uint32)playerCounter;
|
|
differencePacket << (uint32)0;
|
|
differencePacket.append(buffer_players);
|
|
}
|
|
|
|
void LFGMgr::RBPacketBuildFull(WorldPacket& fullPacket, uint32 dungeonId, RBInternalInfoMap& infoMap)
|
|
{
|
|
fullPacket << (uint32)LFG_TYPE_RAID;
|
|
fullPacket << (uint32)dungeonId;
|
|
fullPacket << (uint8)0;
|
|
uint32 groupCounter = 0, playerCounter = 0;
|
|
ByteBuffer buffer_groups, buffer_players;
|
|
for (RBInternalInfoMap::const_iterator itr = infoMap.begin(); itr != infoMap.end(); ++itr)
|
|
{
|
|
if (itr->second.isGroupLeader)
|
|
{
|
|
++groupCounter;
|
|
RBPacketAppendGroup(itr->second, buffer_groups);
|
|
}
|
|
++playerCounter;
|
|
RBPacketAppendPlayer(itr->second, buffer_players);
|
|
}
|
|
fullPacket << (uint32)groupCounter;
|
|
fullPacket << (uint32)0;
|
|
fullPacket.append(buffer_groups);
|
|
fullPacket << (uint32)playerCounter;
|
|
fullPacket << (uint32)0;
|
|
fullPacket.append(buffer_players);
|
|
}
|
|
|
|
// pussywizard:
|
|
void LFGMgr::LeaveAllLfgQueues(ObjectGuid guid, bool allowgroup, ObjectGuid groupguid)
|
|
{
|
|
ObjectGuid pguid, gguid;
|
|
if (guid.IsGroup())
|
|
gguid = guid;
|
|
else if (groupguid && groupguid.IsGroup())
|
|
{
|
|
pguid = guid;
|
|
gguid = groupguid;
|
|
}
|
|
else
|
|
{
|
|
pguid = guid;
|
|
gguid = GetGroup(guid);
|
|
}
|
|
if (!allowgroup)
|
|
gguid.Clear();
|
|
|
|
if (pguid)
|
|
for (lfg::LfgQueueContainer::iterator itr = QueuesStore.begin(); itr != QueuesStore.end(); ++itr)
|
|
itr->second.RemoveFromQueue(pguid);
|
|
if (gguid)
|
|
for (lfg::LfgQueueContainer::iterator itr = QueuesStore.begin(); itr != QueuesStore.end(); ++itr)
|
|
itr->second.RemoveFromQueue(gguid);
|
|
|
|
if (pguid && !gguid)
|
|
{
|
|
if (GetState(pguid) == LFG_STATE_QUEUED)
|
|
{
|
|
SendLfgUpdatePlayer(pguid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
|
|
SetState(pguid, LFG_STATE_NONE);
|
|
}
|
|
}
|
|
if (gguid)
|
|
{
|
|
if (GetState(gguid) == LFG_STATE_QUEUED)
|
|
{
|
|
SetState(gguid, LFG_STATE_NONE);
|
|
const LfgGuidSet& players = GetPlayers(gguid);
|
|
for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
|
|
{
|
|
SetState(*it, LFG_STATE_NONE);
|
|
SendLfgUpdateParty(*it, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Update the Role check info with the player selected role.
|
|
|
|
@param[in] grp Group guid to update rolecheck
|
|
@param[in] guid Player guid (0 = rolecheck failed)
|
|
@param[in] roles Player selected roles
|
|
*/
|
|
void LFGMgr::UpdateRoleCheck(ObjectGuid gguid, ObjectGuid guid /* = 0 */, uint8 roles /* = PLAYER_ROLE_NONE */)
|
|
{
|
|
if (!gguid)
|
|
return;
|
|
|
|
LfgRolesMap check_roles;
|
|
LfgRoleCheckContainer::iterator itRoleCheck = RoleChecksStore.find(gguid);
|
|
if (itRoleCheck == RoleChecksStore.end())
|
|
return;
|
|
|
|
LfgRoleCheck& roleCheck = itRoleCheck->second;
|
|
bool sendRoleChosen = roleCheck.state != LFG_ROLECHECK_DEFAULT && guid;
|
|
|
|
if (!guid)
|
|
roleCheck.state = LFG_ROLECHECK_ABORTED;
|
|
else if (roles < PLAYER_ROLE_TANK) // Player selected no role.
|
|
roleCheck.state = LFG_ROLECHECK_NO_ROLE;
|
|
else
|
|
{
|
|
roleCheck.roles[guid] = roles;
|
|
|
|
// Check if all players have selected a role
|
|
LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin();
|
|
while (itRoles != roleCheck.roles.end() && itRoles->second != PLAYER_ROLE_NONE)
|
|
++itRoles;
|
|
|
|
if (itRoles == roleCheck.roles.end())
|
|
{
|
|
// use temporal var to check roles, CheckGroupRoles modifies the roles
|
|
check_roles = roleCheck.roles;
|
|
roleCheck.state = CheckGroupRoles(check_roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES;
|
|
}
|
|
}
|
|
|
|
LfgDungeonSet dungeons;
|
|
if (roleCheck.rDungeonId)
|
|
dungeons.insert(roleCheck.rDungeonId);
|
|
else
|
|
dungeons = roleCheck.dungeons;
|
|
|
|
LfgJoinResultData joinData = LfgJoinResultData(LFG_JOIN_FAILED, roleCheck.state);
|
|
for (LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it)
|
|
{
|
|
ObjectGuid pguid = it->first;
|
|
|
|
if (sendRoleChosen)
|
|
SendLfgRoleChosen(pguid, guid, roles);
|
|
|
|
SendLfgRoleCheckUpdate(pguid, roleCheck);
|
|
switch (roleCheck.state)
|
|
{
|
|
case LFG_ROLECHECK_INITIALITING:
|
|
continue;
|
|
case LFG_ROLECHECK_FINISHED:
|
|
SetState(pguid, LFG_STATE_QUEUED);
|
|
SetRoles(pguid, it->second);
|
|
SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, GetComment(pguid)));
|
|
break;
|
|
default:
|
|
if (roleCheck.leader == pguid)
|
|
SendLfgJoinResult(pguid, joinData);
|
|
SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ROLECHECK_FAILED));
|
|
RestoreState(pguid, "Rolecheck Failed");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (roleCheck.state == LFG_ROLECHECK_FINISHED)
|
|
{
|
|
SetState(gguid, LFG_STATE_QUEUED);
|
|
LFGQueue& queue = GetQueue(gguid);
|
|
queue.AddQueueData(gguid, time_t(GameTime::GetGameTime().count()), roleCheck.dungeons, roleCheck.roles);
|
|
RoleChecksStore.erase(itRoleCheck);
|
|
}
|
|
else if (roleCheck.state != LFG_ROLECHECK_INITIALITING)
|
|
{
|
|
RestoreState(gguid, "Rolecheck Failed");
|
|
RoleChecksStore.erase(itRoleCheck);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Given a list of dungeons remove the dungeons players have restrictions.
|
|
|
|
@param[in, out] dungeons Dungeons to check restrictions
|
|
@param[in] players Set of players to check their dungeon restrictions
|
|
@param[out] lockMap Map of players Lock status info of given dungeons (Empty if dungeons is not empty)
|
|
*/
|
|
void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap)
|
|
{
|
|
lockMap.clear();
|
|
for (LfgGuidSet::const_iterator it = players.begin(); it != players.end() && !dungeons.empty(); ++it)
|
|
{
|
|
ObjectGuid guid = (*it);
|
|
LfgLockMap const& cachedLockMap = GetLockedDungeons(guid);
|
|
for (LfgLockMap::const_iterator it2 = cachedLockMap.begin(); it2 != cachedLockMap.end() && !dungeons.empty(); ++it2)
|
|
{
|
|
uint32 dungeonId = (it2->first & 0x00FFFFFF); // Compare dungeon ids
|
|
LfgDungeonSet::iterator itDungeon = dungeons.find(dungeonId);
|
|
if (itDungeon != dungeons.end())
|
|
{
|
|
dungeons.erase(itDungeon);
|
|
lockMap[guid][dungeonId] = it2->second;
|
|
}
|
|
}
|
|
}
|
|
if (!dungeons.empty())
|
|
lockMap.clear();
|
|
}
|
|
|
|
uint8 LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true*/)
|
|
{
|
|
if (groles.empty())
|
|
return 0;
|
|
|
|
uint8 damage = 0;
|
|
uint8 tank = 0;
|
|
uint8 healer = 0;
|
|
|
|
if (removeLeaderFlag)
|
|
for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it)
|
|
it->second &= ~PLAYER_ROLE_LEADER;
|
|
|
|
for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it)
|
|
{
|
|
if (it->second == PLAYER_ROLE_NONE)
|
|
return 0;
|
|
|
|
if (it->second & PLAYER_ROLE_DAMAGE)
|
|
{
|
|
if (it->second != PLAYER_ROLE_DAMAGE)
|
|
{
|
|
it->second -= PLAYER_ROLE_DAMAGE;
|
|
if (uint8 x = CheckGroupRoles(groles, false))
|
|
return x;
|
|
it->second += PLAYER_ROLE_DAMAGE;
|
|
}
|
|
else if (damage == LFG_DPS_NEEDED)
|
|
return 0;
|
|
else
|
|
damage++;
|
|
}
|
|
|
|
if (it->second & PLAYER_ROLE_HEALER)
|
|
{
|
|
if (it->second != PLAYER_ROLE_HEALER)
|
|
{
|
|
it->second -= PLAYER_ROLE_HEALER;
|
|
if (uint8 x = CheckGroupRoles(groles, false))
|
|
return x;
|
|
it->second += PLAYER_ROLE_HEALER;
|
|
}
|
|
else if (healer == LFG_HEALERS_NEEDED)
|
|
return 0;
|
|
else
|
|
healer++;
|
|
}
|
|
|
|
if (it->second & PLAYER_ROLE_TANK)
|
|
{
|
|
if (it->second != PLAYER_ROLE_TANK)
|
|
{
|
|
it->second -= PLAYER_ROLE_TANK;
|
|
if (uint8 x = CheckGroupRoles(groles, false))
|
|
return x;
|
|
it->second += PLAYER_ROLE_TANK;
|
|
}
|
|
else if (tank == LFG_TANKS_NEEDED)
|
|
return 0;
|
|
else
|
|
tank++;
|
|
}
|
|
}
|
|
if ((tank + healer + damage) == uint8(groles.size()))
|
|
return (8 * tank + 4 * healer + damage);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Makes a new group given a proposal
|
|
@param[in] proposal Proposal to get info from
|
|
*/
|
|
void LFGMgr::MakeNewGroup(LfgProposal const& proposal)
|
|
{
|
|
LfgGuidList players;
|
|
GuidUnorderedSet playersToTeleport;
|
|
|
|
for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
|
|
{
|
|
ObjectGuid guid = it->first;
|
|
if (guid == proposal.leader)
|
|
players.push_front(guid);
|
|
else
|
|
players.push_back(guid);
|
|
|
|
if (proposal.isNew || GetGroup(guid) != proposal.group)
|
|
playersToTeleport.insert(guid);
|
|
}
|
|
|
|
// Set the dungeon difficulty
|
|
LFGDungeonData const* dungeon = GetLFGDungeon(proposal.dungeonId);
|
|
ASSERT(dungeon);
|
|
|
|
bool isPremadeGroup = false;
|
|
Group* grp = proposal.group ? sGroupMgr->GetGroupByGUID(proposal.group.GetCounter()) : nullptr;
|
|
if (!grp)
|
|
{
|
|
ObjectGuid groupGUID;
|
|
for (ObjectGuid const& guid : players)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
|
|
{
|
|
Group* group = player->GetGroup();
|
|
if (!group || (groupGUID && groupGUID != group->GetGUID()))
|
|
{
|
|
isPremadeGroup = false;
|
|
break;
|
|
}
|
|
|
|
groupGUID = group->GetGUID();
|
|
isPremadeGroup = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
ObjectGuid oldGroupGUID;
|
|
for (LfgGuidList::const_iterator it = players.begin(); it != players.end(); ++it)
|
|
{
|
|
ObjectGuid pguid = (*it);
|
|
Player* player = ObjectAccessor::FindConnectedPlayer(pguid);
|
|
if (!player)
|
|
continue;
|
|
|
|
Group* group = player->GetGroup();
|
|
if (isPremadeGroup && !grp)
|
|
{
|
|
oldGroupGUID = group->GetGUID();
|
|
grp = group;
|
|
grp->ConvertToLFG(false);
|
|
SetState(grp->GetGUID(), LFG_STATE_PROPOSAL);
|
|
}
|
|
|
|
// Xinef: Apply Random Buff
|
|
if (grp && !grp->IsLfgWithBuff())
|
|
{
|
|
if (!group || group->GetGUID() != oldGroupGUID)
|
|
grp->AddLfgBuffFlag();
|
|
else
|
|
oldGroupGUID = group->GetGUID();
|
|
}
|
|
|
|
// Xinef: Store amount of random players player grouped with
|
|
if (group)
|
|
{
|
|
SetRandomPlayersCount(pguid, group->GetMembersCount() >= MAXGROUPSIZE ? 0 : MAXGROUPSIZE - group->GetMembersCount());
|
|
oldGroupGUID = group->GetGUID();
|
|
if (group != grp)
|
|
group->RemoveMember(player->GetGUID());
|
|
}
|
|
else
|
|
SetRandomPlayersCount(pguid, MAXGROUPSIZE - 1);
|
|
|
|
if (!grp)
|
|
{
|
|
grp = new Group();
|
|
grp->ConvertToLFG();
|
|
grp->Create(player);
|
|
ObjectGuid gguid = grp->GetGUID();
|
|
SetState(gguid, LFG_STATE_PROPOSAL);
|
|
sGroupMgr->AddGroup(grp);
|
|
}
|
|
else if (group != grp)
|
|
{
|
|
// pussywizard:
|
|
if (!grp->IsFull())
|
|
grp->AddMember(player);
|
|
//else // some cleanup? LeaveLFG?
|
|
// ;
|
|
}
|
|
|
|
grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role);
|
|
}
|
|
|
|
// pussywizard: crashfix, group wasn't created when iterating players (no player found by guid), proposal is deleted by the calling function
|
|
if (!grp)
|
|
return;
|
|
|
|
grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty));
|
|
ObjectGuid gguid = grp->GetGUID();
|
|
SetDungeon(gguid, dungeon->Entry());
|
|
SetState(gguid, LFG_STATE_DUNGEON);
|
|
|
|
_SaveToDB(gguid);
|
|
|
|
// Select a player inside to be teleported to
|
|
WorldLocation const* teleportLocation = nullptr;
|
|
bool leaderTeleportIncluded = false;
|
|
for (GroupReference* itr = grp->GetFirstMember(); itr != nullptr; itr = itr->next())
|
|
{
|
|
if (Player* plr = itr->GetSource())
|
|
{
|
|
if (grp->IsLeader(plr->GetGUID()) && playersToTeleport.find(plr->GetGUID()) != playersToTeleport.end())
|
|
{
|
|
leaderTeleportIncluded = true;
|
|
}
|
|
|
|
if (plr->GetMapId() == uint32(dungeon->map) && !proposal.isNew)
|
|
{
|
|
teleportLocation = plr;
|
|
break;
|
|
}
|
|
|
|
// Remove from battleground queues
|
|
for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
|
|
{
|
|
if (BattlegroundQueueTypeId bgQueueTypeId = plr->GetBattlegroundQueueTypeId(i))
|
|
{
|
|
if (bgQueueTypeId != BATTLEGROUND_QUEUE_NONE)
|
|
{
|
|
plr->RemoveBattlegroundQueueId(bgQueueTypeId);
|
|
sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId).RemovePlayer(plr->GetGUID(), true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool randomDungeon = false;
|
|
std::vector<Player*> playersTeleported;
|
|
// Teleport Player
|
|
for (GuidUnorderedSet::const_iterator it = playersToTeleport.begin(); it != playersToTeleport.end(); ++it)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindPlayer(*it))
|
|
{
|
|
if (player->GetGroup() != grp) // pussywizard: could not add because group was full
|
|
continue;
|
|
|
|
// Add the cooldown spell if queued for a random dungeon
|
|
// xinef: add aura
|
|
if ((randomDungeon || selectedRandomLfgDungeon(player->GetGUID())) && !player->HasAura(LFG_SPELL_DUNGEON_COOLDOWN))
|
|
{
|
|
randomDungeon = true;
|
|
// if player is debugging, don't add dungeon cooldown
|
|
if (!m_Testing)
|
|
{
|
|
player->AddAura(LFG_SPELL_DUNGEON_COOLDOWN, player);
|
|
}
|
|
}
|
|
|
|
if (player->GetMapId() == uint32(dungeon->map))
|
|
{
|
|
// check instance id with leader
|
|
if (!leaderTeleportIncluded)
|
|
{
|
|
if (InstancePlayerBind* ilb = sInstanceSaveMgr->PlayerGetBoundInstance(grp->GetLeaderGUID(), dungeon->map, player->GetDungeonDifficulty()))
|
|
{
|
|
if (player->GetInstanceId() == ilb->save->GetInstanceId())
|
|
{
|
|
// Do not teleport if in the same map and instance as leader
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove bind to that map
|
|
sInstanceSaveMgr->PlayerUnbindInstance(player->GetGUID(), dungeon->map, player->GetDungeonDifficulty(), true);
|
|
}
|
|
else
|
|
{
|
|
// RDF removes all binds to that map
|
|
if (randomDungeon && !sInstanceSaveMgr->PlayerIsPermBoundToInstance(player->GetGUID(), dungeon->map, player->GetDungeonDifficulty()))
|
|
{
|
|
sInstanceSaveMgr->PlayerUnbindInstance(player->GetGUID(), dungeon->map, player->GetDungeonDifficulty(), true);
|
|
}
|
|
}
|
|
|
|
playersTeleported.push_back(player);
|
|
}
|
|
}
|
|
|
|
for (Player* player : playersTeleported)
|
|
{
|
|
TeleportPlayer(player, false, teleportLocation);
|
|
}
|
|
|
|
if (randomDungeon)
|
|
grp->AddLfgRandomInstanceFlag();
|
|
if (Difficulty(dungeon->difficulty) == DUNGEON_DIFFICULTY_HEROIC)
|
|
grp->AddLfgHeroicFlag();
|
|
|
|
// Update group info
|
|
grp->SendUpdate();
|
|
}
|
|
|
|
uint32 LFGMgr::AddProposal(LfgProposal& proposal)
|
|
{
|
|
proposal.id = ++m_lfgProposalId;
|
|
ProposalsStore[m_lfgProposalId] = proposal;
|
|
return m_lfgProposalId;
|
|
}
|
|
|
|
/**
|
|
Update Proposal info with player answer
|
|
|
|
@param[in] proposalId Proposal id to be updated
|
|
@param[in] guid Player guid to update answer
|
|
@param[in] accept Player answer
|
|
*/
|
|
void LFGMgr::UpdateProposal(uint32 proposalId, ObjectGuid guid, bool accept)
|
|
{
|
|
// Check if the proposal exists
|
|
LfgProposalContainer::iterator itProposal = ProposalsStore.find(proposalId);
|
|
if (itProposal == ProposalsStore.end())
|
|
return;
|
|
|
|
LfgProposal& proposal = itProposal->second;
|
|
|
|
// Check if proposal have the current player
|
|
LfgProposalPlayerContainer::iterator itProposalPlayer = proposal.players.find(guid);
|
|
if (itProposalPlayer == proposal.players.end())
|
|
return;
|
|
|
|
LfgProposalPlayer& player = itProposalPlayer->second;
|
|
player.accept = LfgAnswer(accept);
|
|
|
|
LOG_DEBUG("lfg", "LFGMgr::UpdateProposal: Player [{}] of proposal {} selected: {}", guid.ToString(), proposalId, accept);
|
|
if (!accept)
|
|
{
|
|
RemoveProposal(itProposal, LFG_UPDATETYPE_PROPOSAL_DECLINED);
|
|
return;
|
|
}
|
|
|
|
// check if all have answered and reorder players (leader first)
|
|
bool allAnswered = true;
|
|
for (LfgProposalPlayerContainer::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers)
|
|
if (itPlayers->second.accept != LFG_ANSWER_AGREE) // No answer (-1) or not accepted (0)
|
|
allAnswered = false;
|
|
|
|
if (!m_Testing && !allAnswered)
|
|
{
|
|
for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
|
|
SendLfgUpdateProposal(it->first, proposal);
|
|
|
|
return;
|
|
}
|
|
|
|
bool sendUpdate = proposal.state != LFG_PROPOSAL_SUCCESS;
|
|
proposal.state = LFG_PROPOSAL_SUCCESS;
|
|
time_t joinTime = GameTime::GetGameTime().count();
|
|
|
|
LFGQueue& queue = GetQueue(guid);
|
|
LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_GROUP_FOUND);
|
|
for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
|
|
{
|
|
ObjectGuid pguid = it->first;
|
|
ObjectGuid gguid = it->second.group;
|
|
uint32 dungeonId = (*GetSelectedDungeons(pguid).begin());
|
|
int32 waitTime = -1;
|
|
if (sendUpdate)
|
|
SendLfgUpdateProposal(pguid, proposal);
|
|
|
|
if (gguid)
|
|
{
|
|
waitTime = int32((joinTime - queue.GetJoinTime(gguid)) / IN_MILLISECONDS);
|
|
SendLfgUpdateParty(pguid, updateData);
|
|
}
|
|
else
|
|
{
|
|
waitTime = int32((joinTime - queue.GetJoinTime(pguid)) / IN_MILLISECONDS);
|
|
SendLfgUpdatePlayer(pguid, updateData);
|
|
}
|
|
updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE;
|
|
SendLfgUpdatePlayer(pguid, updateData);
|
|
SendLfgUpdateParty(pguid, updateData);
|
|
|
|
// Update timers
|
|
uint8 role = GetRoles(pguid);
|
|
role &= ~PLAYER_ROLE_LEADER;
|
|
switch (role)
|
|
{
|
|
case PLAYER_ROLE_DAMAGE:
|
|
queue.UpdateWaitTimeDps(waitTime, dungeonId);
|
|
break;
|
|
case PLAYER_ROLE_HEALER:
|
|
queue.UpdateWaitTimeHealer(waitTime, dungeonId);
|
|
break;
|
|
case PLAYER_ROLE_TANK:
|
|
queue.UpdateWaitTimeTank(waitTime, dungeonId);
|
|
break;
|
|
default:
|
|
queue.UpdateWaitTimeAvg(waitTime, dungeonId);
|
|
break;
|
|
}
|
|
|
|
SetState(pguid, LFG_STATE_DUNGEON);
|
|
}
|
|
|
|
// Remove players/groups from Queue
|
|
for (uint8 i = 0; i < 5 && proposal.queues.guids[i]; ++i)
|
|
queue.RemoveQueueData(proposal.queues.guids[i]);
|
|
|
|
MakeNewGroup(proposal);
|
|
ProposalsStore.erase(itProposal);
|
|
}
|
|
|
|
/**
|
|
Remove a proposal from the pool, remove the group that didn't accept (if needed) and readd the other members to the queue
|
|
|
|
@param[in] itProposal Iterator to the proposal to remove
|
|
@param[in] type Type of removal (LFG_UPDATETYPE_PROPOSAL_FAILED, LFG_UPDATETYPE_PROPOSAL_DECLINED)
|
|
*/
|
|
void LFGMgr::RemoveProposal(LfgProposalContainer::iterator itProposal, LfgUpdateType type)
|
|
{
|
|
LfgProposal& proposal = itProposal->second;
|
|
proposal.state = LFG_PROPOSAL_FAILED;
|
|
|
|
LOG_DEBUG("lfg", "LFGMgr::RemoveProposal: Proposal {}, state FAILED, UpdateType {}", itProposal->first, type);
|
|
// Mark all people that didn't answered as no accept
|
|
if (type == LFG_UPDATETYPE_PROPOSAL_FAILED)
|
|
for (LfgProposalPlayerContainer::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
|
|
if (it->second.accept == LFG_ANSWER_PENDING)
|
|
it->second.accept = LFG_ANSWER_DENY;
|
|
|
|
// pussywizard: add cooldown for not accepting (after 40 secs) or declining
|
|
for (LfgProposalPlayerContainer::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
|
|
if (it->second.accept == LFG_ANSWER_DENY)
|
|
if (Player* plr = ObjectAccessor::FindPlayer(it->first))
|
|
if (Aura* aura = plr->AddAura(LFG_SPELL_DUNGEON_COOLDOWN, plr))
|
|
aura->SetDuration(150 * IN_MILLISECONDS);
|
|
|
|
// Mark players/groups to be removed
|
|
LfgGuidSet toRemove;
|
|
for (LfgProposalPlayerContainer::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
|
|
{
|
|
if (it->second.accept == LFG_ANSWER_AGREE)
|
|
continue;
|
|
|
|
ObjectGuid guid = it->second.group ? it->second.group : it->first;
|
|
// Player didn't accept or still pending when no secs left
|
|
if (it->second.accept == LFG_ANSWER_DENY || type == LFG_UPDATETYPE_PROPOSAL_FAILED)
|
|
{
|
|
it->second.accept = LFG_ANSWER_DENY;
|
|
toRemove.insert(guid);
|
|
}
|
|
}
|
|
|
|
// Notify players
|
|
for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
|
|
{
|
|
ObjectGuid guid = it->first;
|
|
ObjectGuid gguid = it->second.group ? it->second.group : guid;
|
|
|
|
SendLfgUpdateProposal(guid, proposal);
|
|
|
|
if (toRemove.find(gguid) != toRemove.end()) // Didn't accept or in same group that someone that didn't accept
|
|
{
|
|
LfgUpdateData updateData;
|
|
if (it->second.accept == LFG_ANSWER_DENY)
|
|
{
|
|
updateData.updateType = type;
|
|
LOG_DEBUG("lfg", "LFGMgr::RemoveProposal: [{}] didn't accept. Removing from queue and compatible cache", guid.ToString());
|
|
}
|
|
else
|
|
{
|
|
updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE;
|
|
LOG_DEBUG("lfg", "LFGMgr::RemoveProposal: [{}] in same group that someone that didn't accept. Removing from queue and compatible cache", guid.ToString());
|
|
}
|
|
|
|
RestoreState(guid, "Proposal Fail (didn't accepted or in group with someone that didn't accept");
|
|
if (gguid != guid)
|
|
{
|
|
RestoreState(it->second.group, "Proposal Fail (someone in group didn't accepted)");
|
|
SendLfgUpdateParty(guid, updateData);
|
|
}
|
|
else
|
|
SendLfgUpdatePlayer(guid, updateData);
|
|
}
|
|
else
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::RemoveProposal: Readding [{}] to queue.", guid.ToString());
|
|
SetState(guid, LFG_STATE_QUEUED);
|
|
if (gguid != guid)
|
|
{
|
|
SetState(gguid, LFG_STATE_QUEUED);
|
|
SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid)));
|
|
}
|
|
else
|
|
SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid)));
|
|
}
|
|
}
|
|
|
|
LFGQueue& queue = GetQueue(proposal.players.begin()->first);
|
|
// Remove players/groups from queue
|
|
for (LfgGuidSet::const_iterator it = toRemove.begin(); it != toRemove.end(); ++it)
|
|
{
|
|
ObjectGuid guid = *it;
|
|
queue.RemoveFromQueue(guid);
|
|
proposal.queues.remove(guid);
|
|
}
|
|
|
|
// Readd to queue
|
|
for (uint8 i = 0; i < 5 && proposal.queues.guids[i]; ++i)
|
|
{
|
|
// xinef: this will work as data is not deleted, only references to this data are cleared
|
|
// xinef: when new proposal is created
|
|
// xinef: successful proposal is also taken into account is similar manner
|
|
queue.AddToQueue(proposal.queues.guids[i], true);
|
|
}
|
|
|
|
ProposalsStore.erase(itProposal);
|
|
}
|
|
|
|
/**
|
|
Initialize a boot kick vote
|
|
|
|
@param[in] gguid Group the vote kicks belongs to
|
|
@param[in] kicker Kicker guid
|
|
@param[in] victim Victim guid
|
|
@param[in] reason Kick reason
|
|
*/
|
|
void LFGMgr::InitBoot(ObjectGuid gguid, ObjectGuid kicker, ObjectGuid victim, std::string const& reason)
|
|
{
|
|
SetState(gguid, LFG_STATE_BOOT);
|
|
|
|
LfgPlayerBoot& boot = BootsStore[gguid];
|
|
boot.inProgress = true;
|
|
boot.cancelTime = time_t(GameTime::GetGameTime().count()) + LFG_TIME_BOOT;
|
|
boot.reason = reason;
|
|
boot.victim = victim;
|
|
|
|
LfgGuidSet const& players = GetPlayers(gguid);
|
|
|
|
// Set votes
|
|
for (LfgGuidSet::const_iterator itr = players.begin(); itr != players.end(); ++itr)
|
|
{
|
|
ObjectGuid guid = (*itr);
|
|
SetState(guid, LFG_STATE_BOOT);
|
|
boot.votes[guid] = LFG_ANSWER_PENDING;
|
|
}
|
|
|
|
boot.votes[victim] = LFG_ANSWER_DENY; // Victim auto vote NO
|
|
boot.votes[kicker] = LFG_ANSWER_AGREE; // Kicker auto vote YES
|
|
|
|
// Notify players
|
|
for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
|
|
SendLfgBootProposalUpdate(*it, boot);
|
|
}
|
|
|
|
/**
|
|
Update Boot info with player answer
|
|
|
|
@param[in] guid Player who has answered
|
|
@param[in] player answer
|
|
*/
|
|
void LFGMgr::UpdateBoot(ObjectGuid guid, bool accept)
|
|
{
|
|
ObjectGuid gguid = GetGroup(guid);
|
|
if (!gguid)
|
|
return;
|
|
|
|
LfgPlayerBootContainer::iterator itBoot = BootsStore.find(gguid);
|
|
if (itBoot == BootsStore.end())
|
|
return;
|
|
|
|
LfgPlayerBoot& boot = itBoot->second;
|
|
|
|
if (boot.votes[guid] != LFG_ANSWER_PENDING) // Cheat check: Player can't vote twice
|
|
return;
|
|
|
|
boot.votes[guid] = LfgAnswer(accept);
|
|
|
|
uint8 votesNum = 0;
|
|
uint8 agreeNum = 0;
|
|
for (LfgAnswerContainer::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes)
|
|
{
|
|
if (itVotes->second != LFG_ANSWER_PENDING)
|
|
{
|
|
++votesNum;
|
|
if (itVotes->second == LFG_ANSWER_AGREE)
|
|
++agreeNum;
|
|
}
|
|
}
|
|
|
|
// if we don't have enough votes (agree or deny) do nothing
|
|
if (agreeNum < LFG_GROUP_KICK_VOTES_NEEDED && (votesNum - agreeNum) < LFG_GROUP_KICK_VOTES_NEEDED)
|
|
return;
|
|
|
|
// Send update info to all players
|
|
boot.inProgress = false;
|
|
for (LfgAnswerContainer::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes)
|
|
{
|
|
ObjectGuid pguid = itVotes->first;
|
|
if (pguid != boot.victim)
|
|
{
|
|
SetState(pguid, LFG_STATE_DUNGEON);
|
|
SendLfgBootProposalUpdate(pguid, boot);
|
|
}
|
|
}
|
|
|
|
SetState(gguid, LFG_STATE_DUNGEON);
|
|
if (agreeNum == LFG_GROUP_KICK_VOTES_NEEDED) // Vote passed - Kick player
|
|
{
|
|
if (Group* group = sGroupMgr->GetGroupByGUID(gguid.GetCounter()))
|
|
Player::RemoveFromGroup(group, boot.victim, GROUP_REMOVEMETHOD_KICK_LFG);
|
|
|
|
DecreaseKicksLeft(gguid);
|
|
}
|
|
BootsStore.erase(itBoot);
|
|
}
|
|
|
|
/**
|
|
Teleports the player in or out the dungeon
|
|
|
|
@param[in] player Player to teleport
|
|
@param[in] out Teleport out (true) or in (false)
|
|
@param[in] fromOpcode Function called from opcode handlers? (Default false)
|
|
*/
|
|
void LFGMgr::TeleportPlayer(Player* player, bool out, WorldLocation const* teleportLocation /*= nullptr*/)
|
|
{
|
|
LFGDungeonData const* dungeon = nullptr;
|
|
Group* group = player->GetGroup();
|
|
|
|
if (group && group->isLFGGroup())
|
|
dungeon = GetLFGDungeon(GetDungeon(group->GetGUID()));
|
|
|
|
if (!dungeon)
|
|
{
|
|
player->GetSession()->SendLfgTeleportError(uint8(LFG_TELEPORTERROR_INVALID_LOCATION));
|
|
return;
|
|
}
|
|
|
|
if (out)
|
|
{
|
|
if (player->GetMapId() == uint32(dungeon->map))
|
|
player->TeleportToEntryPoint();
|
|
|
|
return;
|
|
}
|
|
|
|
LfgTeleportError error = LFG_TELEPORTERROR_OK;
|
|
|
|
if (!player->IsAlive())
|
|
{
|
|
error = LFG_TELEPORTERROR_PLAYER_DEAD;
|
|
}
|
|
else if (player->IsFalling() || player->HasUnitState(UNIT_STATE_JUMPING))
|
|
{
|
|
error = LFG_TELEPORTERROR_FALLING;
|
|
}
|
|
else if (player->IsMirrorTimerActive(FATIGUE_TIMER))
|
|
{
|
|
error = LFG_TELEPORTERROR_FATIGUE;
|
|
}
|
|
else if (player->GetVehicle())
|
|
{
|
|
error = LFG_TELEPORTERROR_IN_VEHICLE;
|
|
}
|
|
else if (player->GetCharmGUID() || player->IsInCombat())
|
|
{
|
|
error = LFG_TELEPORTERROR_COMBAT;
|
|
}
|
|
else
|
|
{
|
|
uint32 mapid = dungeon->map;
|
|
float x = dungeon->x;
|
|
float y = dungeon->y;
|
|
float z = dungeon->z;
|
|
float orientation = dungeon->o;
|
|
|
|
if (teleportLocation)
|
|
{
|
|
teleportLocation->GetWorldLocation(mapid, x, y, z, orientation);
|
|
}
|
|
|
|
if (!player->GetMap()->IsDungeon() || player->GetEntryPoint().GetMapId() == MAPID_INVALID)
|
|
{
|
|
player->SetEntryPoint();
|
|
}
|
|
|
|
if (!player->TeleportTo(mapid, x, y, z, orientation, 0, nullptr, mapid == player->GetMapId()))
|
|
{
|
|
error = LFG_TELEPORTERROR_INVALID_LOCATION;
|
|
}
|
|
}
|
|
|
|
if (error != LFG_TELEPORTERROR_OK)
|
|
player->GetSession()->SendLfgTeleportError(uint8(error));
|
|
|
|
//LOG_DEBUG("lfg", "TeleportPlayer: Player {} is being teleported in to map {} "
|
|
// "(x: {}, y: {}, z: {}) Result: {}", player->GetName(), dungeon->map,
|
|
// dungeon->x, dungeon->y, dungeon->z, error);
|
|
}
|
|
|
|
/**
|
|
Finish a dungeon and give reward, if any.
|
|
|
|
@param[in] guid Group guid
|
|
@param[in] dungeonId Dungeonid
|
|
*/
|
|
void LFGMgr::FinishDungeon(ObjectGuid gguid, const uint32 dungeonId, const Map* currMap)
|
|
{
|
|
uint32 gDungeonId = GetDungeon(gguid);
|
|
if (gDungeonId != dungeonId)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::FinishDungeon: [{}] Finished dungeon {} but group queued for {}. Ignoring", gguid.ToString(), dungeonId, gDungeonId);
|
|
return;
|
|
}
|
|
|
|
if (GetState(gguid) == LFG_STATE_FINISHED_DUNGEON) // Shouldn't happen. Do not reward multiple times
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::FinishDungeon: [{}] Already rewarded group. Ignoring", gguid.ToString());
|
|
return;
|
|
}
|
|
|
|
SetState(gguid, LFG_STATE_FINISHED_DUNGEON);
|
|
_SaveToDB(gguid); // pussywizard
|
|
|
|
const LfgGuidSet& players = GetPlayers(gguid);
|
|
for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
|
|
{
|
|
ObjectGuid guid = (*it);
|
|
if (GetState(guid) == LFG_STATE_FINISHED_DUNGEON)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::FinishDungeon: [{}] Already rewarded player. Ignoring", guid.ToString());
|
|
continue;
|
|
}
|
|
|
|
uint32 rDungeonId = 0;
|
|
const LfgDungeonSet& dungeons = GetSelectedDungeons(guid);
|
|
if (!dungeons.empty())
|
|
rDungeonId = (*dungeons.begin());
|
|
|
|
SetState(guid, LFG_STATE_FINISHED_DUNGEON);
|
|
|
|
// Give rewards only if its a random dungeon
|
|
LFGDungeonData const* dungeon = GetLFGDungeon(rDungeonId);
|
|
|
|
if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM && !dungeon->seasonal))
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::FinishDungeon: [{}] dungeon {} is not random or seasonal", guid.ToString(), rDungeonId);
|
|
continue;
|
|
}
|
|
|
|
Player* player = ObjectAccessor::FindPlayer(guid);
|
|
if (!player || player->FindMap() != currMap) // pussywizard: currMap - multithreading crash if on other map (map id check is not enough, binding system is not reliable)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::FinishDungeon: [{}] not found in world", guid.ToString());
|
|
continue;
|
|
}
|
|
|
|
LFGDungeonData const* dungeonDone = GetLFGDungeon(dungeonId);
|
|
uint32 mapId = dungeonDone ? uint32(dungeonDone->map) : 0;
|
|
|
|
if (player->GetMapId() != mapId)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::FinishDungeon: [{}] is in map {} and should be in {} to get reward", guid.ToString(), player->GetMapId(), mapId);
|
|
continue;
|
|
}
|
|
|
|
// Remove Dungeon Finder Cooldown if still exists
|
|
if (player->HasAura(LFG_SPELL_DUNGEON_COOLDOWN))
|
|
{
|
|
player->RemoveAurasDueToSpell(LFG_SPELL_DUNGEON_COOLDOWN);
|
|
}
|
|
|
|
// Xinef: Update achievements, set correct amount of randomly grouped players
|
|
if (dungeon->difficulty == DUNGEON_DIFFICULTY_HEROIC)
|
|
if (uint8 count = GetRandomPlayersCount(player->GetGUID()))
|
|
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS, count);
|
|
|
|
LfgReward const* reward = GetRandomDungeonReward(rDungeonId, player->GetLevel());
|
|
if (!reward)
|
|
continue;
|
|
|
|
bool done = false;
|
|
Quest const* quest = sObjectMgr->GetQuestTemplate(reward->firstQuest);
|
|
if (!quest)
|
|
continue;
|
|
|
|
// if we can take the quest, means that we haven't done this kind of "run", IE: First Heroic Random of Day.
|
|
if (player->CanRewardQuest(quest, false))
|
|
player->RewardQuest(quest, 0, nullptr, false, true);
|
|
else
|
|
{
|
|
done = true;
|
|
quest = sObjectMgr->GetQuestTemplate(reward->otherQuest);
|
|
if (!quest)
|
|
continue;
|
|
// we give reward without informing client (retail does this)
|
|
player->RewardQuest(quest, 0, nullptr, false, true);
|
|
}
|
|
|
|
// Give rewards
|
|
LOG_DEBUG("lfg", "LFGMgr::FinishDungeon: [{}] done dungeon {}, {} previously done.", player->GetGUID().ToString(), GetDungeon(gguid), done ? " " : " not");
|
|
LfgPlayerRewardData data = LfgPlayerRewardData(dungeon->Entry(), GetDungeon(gguid, false), done, quest);
|
|
player->GetSession()->SendLfgPlayerReward(data);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------//
|
|
// Auxiliar Functions
|
|
// --------------------------------------------------------------------------//
|
|
|
|
/**
|
|
Get the dungeon list that can be done given a random dungeon entry.
|
|
|
|
@param[in] randomdungeon Random dungeon id (if value = 0 will return all dungeons)
|
|
@returns Set of dungeons that can be done.
|
|
*/
|
|
LfgDungeonSet const& LFGMgr::GetDungeonsByRandom(uint32 randomdungeon)
|
|
{
|
|
LFGDungeonData const* dungeon = GetLFGDungeon(randomdungeon);
|
|
uint32 group = dungeon ? dungeon->group : 0;
|
|
return CachedDungeonMapStore[group];
|
|
}
|
|
|
|
/**
|
|
Get the reward of a given random dungeon at a certain level
|
|
|
|
@param[in] dungeon dungeon id
|
|
@param[in] level Player level
|
|
@returns Reward
|
|
*/
|
|
LfgReward const* LFGMgr::GetRandomDungeonReward(uint32 dungeon, uint8 level)
|
|
{
|
|
LfgReward const* rew = nullptr;
|
|
LfgRewardContainerBounds bounds = RewardMapStore.equal_range(dungeon & 0x00FFFFFF);
|
|
for (LfgRewardContainer::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
|
|
{
|
|
rew = itr->second;
|
|
// ordered properly at loading
|
|
if (itr->second->maxLevel >= level)
|
|
break;
|
|
}
|
|
|
|
return rew;
|
|
}
|
|
|
|
/**
|
|
Given a Dungeon id returns the dungeon Type
|
|
|
|
@param[in] dungeon dungeon id
|
|
@returns Dungeon type
|
|
*/
|
|
LfgType LFGMgr::GetDungeonType(uint32 dungeonId)
|
|
{
|
|
LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId);
|
|
if (!dungeon)
|
|
return LFG_TYPE_NONE;
|
|
|
|
return LfgType(dungeon->type);
|
|
}
|
|
|
|
LfgState LFGMgr::GetState(ObjectGuid guid)
|
|
{
|
|
LfgState state;
|
|
if (guid.IsGroup())
|
|
state = GroupsStore[guid].GetState();
|
|
else
|
|
state = PlayersStore[guid].GetState();
|
|
|
|
LOG_DEBUG("lfg", "LFGMgr::GetState: [{}] = {}", guid.ToString(), state);
|
|
return state;
|
|
}
|
|
|
|
LfgState LFGMgr::GetOldState(ObjectGuid guid)
|
|
{
|
|
LfgState state;
|
|
if (guid.IsGroup())
|
|
state = GroupsStore[guid].GetOldState();
|
|
else
|
|
state = PlayersStore[guid].GetOldState();
|
|
|
|
LOG_DEBUG("lfg", "LFGMgr::GetOldState: [{}] = {}", guid.ToString(), state);
|
|
return state;
|
|
}
|
|
|
|
uint32 LFGMgr::GetDungeon(ObjectGuid guid, bool asId /*= true */)
|
|
{
|
|
uint32 dungeon = GroupsStore[guid].GetDungeon(asId);
|
|
LOG_DEBUG("lfg", "LFGMgr::GetDungeon: [{}] asId: {} = {}", guid.ToString(), asId, dungeon);
|
|
return dungeon;
|
|
}
|
|
|
|
uint32 LFGMgr::GetDungeonMapId(ObjectGuid guid)
|
|
{
|
|
uint32 dungeonId = GroupsStore[guid].GetDungeon(true);
|
|
uint32 mapId = 0;
|
|
if (dungeonId)
|
|
if (LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId))
|
|
mapId = dungeon->map;
|
|
|
|
LOG_DEBUG("lfg", "LFGMgr::GetDungeonMapId: [{}] = {} (DungeonId = {})", guid.ToString(), mapId, dungeonId);
|
|
return mapId;
|
|
}
|
|
|
|
uint8 LFGMgr::GetRoles(ObjectGuid guid)
|
|
{
|
|
uint8 roles = PlayersStore[guid].GetRoles();
|
|
LOG_DEBUG("lfg", "LFGMgr::GetRoles: [{}] = {}", guid.ToString(), roles);
|
|
return roles;
|
|
}
|
|
|
|
const std::string& LFGMgr::GetComment(ObjectGuid guid)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::GetComment: [{}] = {}", guid.ToString(), PlayersStore[guid].GetComment());
|
|
return PlayersStore[guid].GetComment();
|
|
}
|
|
|
|
LfgDungeonSet const& LFGMgr::GetSelectedDungeons(ObjectGuid guid)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::GetSelectedDungeons: [{}]", guid.ToString());
|
|
return PlayersStore[guid].GetSelectedDungeons();
|
|
}
|
|
|
|
LfgLockMap const& LFGMgr::GetLockedDungeons(ObjectGuid guid)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::GetLockedDungeons: [{}]", guid.ToString());
|
|
return PlayersStore[guid].GetLockedDungeons();
|
|
}
|
|
|
|
uint8 LFGMgr::GetKicksLeft(ObjectGuid guid)
|
|
{
|
|
uint8 kicks = GroupsStore[guid].GetKicksLeft();
|
|
LOG_DEBUG("lfg", "LFGMgr::GetKicksLeft: [{}] = {}", guid.ToString(), kicks);
|
|
return kicks;
|
|
}
|
|
|
|
void LFGMgr::RestoreState(ObjectGuid guid, char const* /*debugMsg*/)
|
|
{
|
|
if (guid.IsGroup())
|
|
{
|
|
LfgGroupData& data = GroupsStore[guid];
|
|
/*if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG))
|
|
{
|
|
std::string const& ps = GetStateString(data.GetState());
|
|
std::string const& os = GetStateString(data.GetOldState());
|
|
LOG_TRACE("lfg", "LFGMgr::RestoreState: Group: [{}] ({}) State: {}, oldState: {}",
|
|
guid.ToString(), debugMsg, ps, os);
|
|
}*/
|
|
|
|
data.RestoreState();
|
|
}
|
|
else
|
|
{
|
|
LfgPlayerData& data = PlayersStore[guid];
|
|
/*if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG))
|
|
{
|
|
std::string const& ps = GetStateString(data.GetState());
|
|
std::string const& os = GetStateString(data.GetOldState());
|
|
LOG_TRACE("lfg", "LFGMgr::RestoreState: Player: [{}] ({}) State: {}, oldState: {}",
|
|
guid.ToString(), debugMsg, ps, os);
|
|
}*/
|
|
data.RestoreState();
|
|
}
|
|
}
|
|
|
|
void LFGMgr::SetState(ObjectGuid guid, LfgState state)
|
|
{
|
|
if (guid.IsGroup())
|
|
{
|
|
LfgGroupData& data = GroupsStore[guid];
|
|
std::string ns = GetStateString(state);
|
|
std::string ps = GetStateString(data.GetState());
|
|
std::string os = GetStateString(data.GetOldState());
|
|
LOG_DEBUG("lfg", "LFGMgr::SetState: Group: [{}] newState: {}, previous: {}, oldState: {}", guid.ToString(), ns, ps, os);
|
|
data.SetState(state);
|
|
}
|
|
else
|
|
{
|
|
LfgPlayerData& data = PlayersStore[guid];
|
|
std::string ns = GetStateString(state);
|
|
std::string ps = GetStateString(data.GetState());
|
|
std::string os = GetStateString(data.GetOldState());
|
|
LOG_DEBUG("lfg", "LFGMgr::SetState: Player: [{}] newState: {}, previous: {}, oldState: {}", guid.ToString(), ns, ps, os);
|
|
data.SetState(state);
|
|
}
|
|
}
|
|
|
|
void LFGMgr::SetCanOverrideRBState(ObjectGuid guid, bool val)
|
|
{
|
|
PlayersStore[guid].SetCanOverrideRBState(val);
|
|
}
|
|
|
|
void LFGMgr::SetDungeon(ObjectGuid guid, uint32 dungeon)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::SetDungeon: [{}] dungeon {}", guid.ToString(), dungeon);
|
|
GroupsStore[guid].SetDungeon(dungeon);
|
|
}
|
|
|
|
void LFGMgr::SetRoles(ObjectGuid guid, uint8 roles)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::SetRoles: [{}] roles: {}", guid.ToString(), roles);
|
|
PlayersStore[guid].SetRoles(roles);
|
|
}
|
|
|
|
void LFGMgr::SetComment(ObjectGuid guid, std::string const& comment)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::SetComment: [{}] comment: {}", guid.ToString(), comment);
|
|
PlayersStore[guid].SetComment(comment);
|
|
}
|
|
|
|
void LFGMgr::LfrSetComment(Player* p, std::string comment)
|
|
{
|
|
// pussywizard: client limit for comment length is 64 @ 3.3.5a
|
|
if (comment.size() > 64)
|
|
comment = comment.substr(0, 64);
|
|
|
|
uint8 teamId = p->GetTeamId();
|
|
RBEntryInfoMap::iterator iter;
|
|
for (RBStoreMap::iterator itr = RaidBrowserStore[teamId].begin(); itr != RaidBrowserStore[teamId].end(); ++itr)
|
|
if ((iter = itr->second.find(p->GetGUID())) != itr->second.end())
|
|
iter->second.comment = comment;
|
|
}
|
|
|
|
void LFGMgr::SetSelectedDungeons(ObjectGuid guid, LfgDungeonSet const& dungeons)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::SetLockedDungeons: [{}]", guid.ToString());
|
|
PlayersStore[guid].SetSelectedDungeons(dungeons);
|
|
}
|
|
|
|
void LFGMgr::SetLockedDungeons(ObjectGuid guid, LfgLockMap const& lock)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::SetLockedDungeons: [{}]", guid.ToString());
|
|
PlayersStore[guid].SetLockedDungeons(lock);
|
|
}
|
|
|
|
void LFGMgr::DecreaseKicksLeft(ObjectGuid guid)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::DecreaseKicksLeft: [{}]", guid.ToString());
|
|
GroupsStore[guid].DecreaseKicksLeft();
|
|
}
|
|
|
|
void LFGMgr::RemoveGroupData(ObjectGuid guid)
|
|
{
|
|
LOG_DEBUG("lfg", "LFGMgr::RemoveGroupData: [{}]", guid.ToString());
|
|
LfgGroupDataContainer::iterator it = GroupsStore.find(guid);
|
|
if (it == GroupsStore.end())
|
|
return;
|
|
|
|
LfgState state = GetState(guid);
|
|
// If group is being formed after proposal success do nothing more
|
|
LfgGuidSet const& players = it->second.GetPlayers();
|
|
for (auto iterator = players.begin(); iterator != players.end(); ++iterator)
|
|
{
|
|
ObjectGuid objectGuid = (*iterator);
|
|
SetGroup(*iterator, ObjectGuid::Empty);
|
|
if (state != LFG_STATE_PROPOSAL)
|
|
{
|
|
SetState(*iterator, LFG_STATE_NONE);
|
|
SendLfgUpdateParty(objectGuid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
|
|
}
|
|
}
|
|
GroupsStore.erase(it);
|
|
}
|
|
|
|
TeamId LFGMgr::GetTeam(ObjectGuid guid)
|
|
{
|
|
return PlayersStore[guid].GetTeam();
|
|
}
|
|
|
|
uint8 LFGMgr::RemovePlayerFromGroup(ObjectGuid gguid, ObjectGuid guid)
|
|
{
|
|
return GroupsStore[gguid].RemovePlayer(guid);
|
|
}
|
|
|
|
void LFGMgr::AddPlayerToGroup(ObjectGuid gguid, ObjectGuid guid)
|
|
{
|
|
GroupsStore[gguid].AddPlayer(guid);
|
|
}
|
|
|
|
void LFGMgr::SetLeader(ObjectGuid gguid, ObjectGuid leader)
|
|
{
|
|
GroupsStore[gguid].SetLeader(leader);
|
|
}
|
|
|
|
void LFGMgr::SetTeam(ObjectGuid guid, TeamId teamId)
|
|
{
|
|
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP))
|
|
teamId = TEAM_ALLIANCE; // @Not Sure About That TeamId is supposed to be uint8 Team = 0(@TrinityCore)
|
|
|
|
PlayersStore[guid].SetTeam(teamId);
|
|
}
|
|
|
|
ObjectGuid LFGMgr::GetGroup(ObjectGuid guid)
|
|
{
|
|
return PlayersStore[guid].GetGroup();
|
|
}
|
|
|
|
void LFGMgr::SetGroup(ObjectGuid guid, ObjectGuid group)
|
|
{
|
|
PlayersStore[guid].SetGroup(group);
|
|
}
|
|
|
|
LfgGuidSet const& LFGMgr::GetPlayers(ObjectGuid guid)
|
|
{
|
|
return GroupsStore[guid].GetPlayers();
|
|
}
|
|
|
|
uint8 LFGMgr::GetPlayerCount(ObjectGuid guid)
|
|
{
|
|
return GroupsStore[guid].GetPlayerCount();
|
|
}
|
|
|
|
ObjectGuid LFGMgr::GetLeader(ObjectGuid guid)
|
|
{
|
|
return GroupsStore[guid].GetLeader();
|
|
}
|
|
|
|
void LFGMgr::SetRandomPlayersCount(ObjectGuid guid, uint8 count)
|
|
{
|
|
PlayersStore[guid].SetRandomPlayersCount(count);
|
|
}
|
|
|
|
uint8 LFGMgr::GetRandomPlayersCount(ObjectGuid guid)
|
|
{
|
|
return PlayersStore[guid].GetRandomPlayersCount();
|
|
}
|
|
|
|
bool LFGMgr::HasIgnore(ObjectGuid guid1, ObjectGuid guid2)
|
|
{
|
|
Player* plr1 = ObjectAccessor::FindConnectedPlayer(guid1);
|
|
Player* plr2 = ObjectAccessor::FindConnectedPlayer(guid2);
|
|
return plr1 && plr2 && (plr1->GetSocial()->HasIgnore(guid2) || plr2->GetSocial()->HasIgnore(guid1));
|
|
}
|
|
|
|
void LFGMgr::SendLfgRoleChosen(ObjectGuid guid, ObjectGuid pguid, uint8 roles)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
|
|
player->GetSession()->SendLfgRoleChosen(pguid, roles);
|
|
}
|
|
|
|
void LFGMgr::SendLfgRoleCheckUpdate(ObjectGuid guid, LfgRoleCheck const& roleCheck)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
|
|
player->GetSession()->SendLfgRoleCheckUpdate(roleCheck);
|
|
}
|
|
|
|
void LFGMgr::SendLfgUpdatePlayer(ObjectGuid guid, LfgUpdateData const& data)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
|
|
player->GetSession()->SendLfgUpdatePlayer(data);
|
|
}
|
|
|
|
void LFGMgr::SendLfgUpdateParty(ObjectGuid guid, LfgUpdateData const& data)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
|
|
player->GetSession()->SendLfgUpdateParty(data);
|
|
}
|
|
|
|
void LFGMgr::SendLfgJoinResult(ObjectGuid guid, LfgJoinResultData const& data)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
|
|
player->GetSession()->SendLfgJoinResult(data);
|
|
}
|
|
|
|
void LFGMgr::SendLfgBootProposalUpdate(ObjectGuid guid, LfgPlayerBoot const& boot)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
|
|
player->GetSession()->SendLfgBootProposalUpdate(boot);
|
|
}
|
|
|
|
void LFGMgr::SendLfgUpdateProposal(ObjectGuid guid, LfgProposal const& proposal)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
|
|
player->GetSession()->SendLfgUpdateProposal(proposal);
|
|
}
|
|
|
|
void LFGMgr::SendLfgQueueStatus(ObjectGuid guid, LfgQueueStatusData const& data)
|
|
{
|
|
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
|
|
player->GetSession()->SendLfgQueueStatus(data);
|
|
}
|
|
|
|
bool LFGMgr::IsLfgGroup(ObjectGuid guid)
|
|
{
|
|
return guid && guid.IsGroup() && GroupsStore[guid].IsLfgGroup();
|
|
}
|
|
|
|
LFGQueue& LFGMgr::GetQueue(ObjectGuid guid)
|
|
{
|
|
uint8 queueId = 0;
|
|
if (guid.IsGroup())
|
|
{
|
|
LfgGuidSet const& players = GetPlayers(guid);
|
|
ObjectGuid pguid = players.empty() ? ObjectGuid::Empty : (*players.begin());
|
|
if (pguid)
|
|
queueId = GetTeam(pguid);
|
|
else
|
|
queueId = GetTeam(GetLeader(guid));
|
|
}
|
|
else
|
|
queueId = GetTeam(guid);
|
|
return QueuesStore[queueId];
|
|
}
|
|
|
|
bool LFGMgr::AllQueued(Lfg5Guids const& check)
|
|
{
|
|
bool ok = true;
|
|
|
|
if (check.empty())
|
|
return false;
|
|
|
|
for (uint8 i = 0; i < 5 && check.guids[i]; ++i)
|
|
{
|
|
ObjectGuid guid = check.guids[i];
|
|
if (GetState(guid) != LFG_STATE_QUEUED)
|
|
{
|
|
LFGQueue& queue = GetQueue(guid);
|
|
queue.RemoveFromQueue(guid);
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
// Only for debugging purposes
|
|
void LFGMgr::Clean()
|
|
{
|
|
QueuesStore.clear();
|
|
}
|
|
|
|
bool LFGMgr::isOptionEnabled(uint32 option)
|
|
{
|
|
return m_options & option;
|
|
}
|
|
|
|
uint32 LFGMgr::GetOptions()
|
|
{
|
|
return m_options;
|
|
}
|
|
|
|
void LFGMgr::SetOptions(uint32 options)
|
|
{
|
|
m_options = options;
|
|
}
|
|
|
|
LfgUpdateData LFGMgr::GetLfgStatus(ObjectGuid guid)
|
|
{
|
|
LfgPlayerData& playerData = PlayersStore[guid];
|
|
return LfgUpdateData(LFG_UPDATETYPE_UPDATE_STATUS, playerData.GetState(), playerData.GetSelectedDungeons());
|
|
}
|
|
|
|
bool LFGMgr::IsSeasonActive(uint32 dungeonId)
|
|
{
|
|
switch (dungeonId)
|
|
{
|
|
case LFG_DUNGEON_HEADLESS_HORSEMAN:
|
|
return IsHolidayActive(HOLIDAY_HALLOWS_END);
|
|
case LFG_DUNGEON_FROST_LORD_AHUNE:
|
|
return IsHolidayActive(HOLIDAY_FIRE_FESTIVAL);
|
|
case LFG_DUNGEON_COREN_DIREBREW:
|
|
return IsHolidayActive(HOLIDAY_BREWFEST);
|
|
case LFG_DUNGEON_CROWN_CHEMICAL_CO:
|
|
return IsHolidayActive(HOLIDAY_LOVE_IS_IN_THE_AIR);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void LFGMgr::SetupGroupMember(ObjectGuid guid, ObjectGuid gguid)
|
|
{
|
|
LfgDungeonSet dungeons;
|
|
dungeons.insert(GetDungeon(gguid));
|
|
SetSelectedDungeons(guid, dungeons);
|
|
SetState(guid, GetState(gguid));
|
|
SetGroup(guid, gguid);
|
|
AddPlayerToGroup(gguid, guid);
|
|
}
|
|
|
|
bool LFGMgr::selectedRandomLfgDungeon(ObjectGuid guid)
|
|
{
|
|
if (GetState(guid) != LFG_STATE_NONE)
|
|
{
|
|
LfgDungeonSet const& dungeons = GetSelectedDungeons(guid);
|
|
if (!dungeons.empty())
|
|
{
|
|
LFGDungeonData const* dungeon = GetLFGDungeon(*dungeons.begin());
|
|
if (dungeon && (dungeon->type == LFG_TYPE_RANDOM || dungeon->seasonal))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool LFGMgr::inLfgDungeonMap(ObjectGuid guid, uint32 map, Difficulty difficulty)
|
|
{
|
|
if (!guid.IsGroup())
|
|
guid = GetGroup(guid);
|
|
|
|
if (uint32 dungeonId = GetDungeon(guid, true))
|
|
if (LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId))
|
|
if (uint32(dungeon->map) == map && dungeon->difficulty == difficulty)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
uint32 LFGMgr::GetLFGDungeonEntry(uint32 id)
|
|
{
|
|
if (id)
|
|
if (LFGDungeonData const* dungeon = GetLFGDungeon(id))
|
|
return dungeon->Entry();
|
|
|
|
return 0;
|
|
}
|
|
|
|
LfgDungeonSet LFGMgr::GetRandomAndSeasonalDungeons(uint8 level, uint8 expansion)
|
|
{
|
|
LfgDungeonSet randomDungeons;
|
|
for (lfg::LFGDungeonContainer::const_iterator itr = LfgDungeonStore.begin(); itr != LfgDungeonStore.end(); ++itr)
|
|
{
|
|
lfg::LFGDungeonData const& dungeon = itr->second;
|
|
if ((dungeon.type == lfg::LFG_TYPE_RANDOM || (dungeon.seasonal && sLFGMgr->IsSeasonActive(dungeon.id)))
|
|
&& dungeon.expansion <= expansion && dungeon.minlevel <= level && level <= dungeon.maxlevel)
|
|
randomDungeons.insert(dungeon.Entry());
|
|
}
|
|
return randomDungeons;
|
|
}
|
|
|
|
} // namespace lfg
|