/*
* Copyright (C) 2005-2009 MaNGOS
*
* Copyright (C) 2008-2010 Trinity
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "ObjectMgr.h"
#include "World.h"
#include "WorldPacket.h"
#include "Policies/SingletonImp.h"
#include "ArenaTeam.h"
#include "BattleGroundMgr.h"
#include "BattleGroundAV.h"
#include "BattleGroundAB.h"
#include "BattleGroundEY.h"
#include "BattleGroundWS.h"
#include "BattleGroundNA.h"
#include "BattleGroundBE.h"
#include "BattleGroundAA.h"
#include "BattleGroundRL.h"
#include "BattleGroundSA.h"
#include "BattleGroundDS.h"
#include "BattleGroundRV.h"
#include "BattleGroundIC.h"
#include "BattleGroundABG.h"
#include "Chat.h"
#include "Map.h"
#include "MapInstanced.h"
#include "MapManager.h"
#include "Player.h"
#include "GameEventMgr.h"
#include "ProgressBar.h"
#include "SharedDefines.h"
INSTANTIATE_SINGLETON_1(BattleGroundMgr);
/*********************************************************/
/*** BATTLEGROUND QUEUE SYSTEM ***/
/*********************************************************/
BattleGroundQueue::BattleGroundQueue()
{
for (uint32 i = 0; i < BG_TEAMS_COUNT; ++i)
{
for (uint32 j = 0; j < MAX_BATTLEGROUND_BRACKETS; ++j)
{
m_SumOfWaitTimes[i][j] = 0;
m_WaitTimeLastPlayer[i][j] = 0;
for (uint32 k = 0; k < COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; ++k)
m_WaitTimes[i][j][k] = 0;
}
}
}
BattleGroundQueue::~BattleGroundQueue()
{
m_QueuedPlayers.clear();
for (int i = 0; i < MAX_BATTLEGROUND_BRACKETS; ++i)
{
for (uint32 j = 0; j < BG_QUEUE_GROUP_TYPES_COUNT; ++j)
{
for (GroupsQueueType::iterator itr = m_QueuedGroups[i][j].begin(); itr!= m_QueuedGroups[i][j].end(); ++itr)
delete (*itr);
m_QueuedGroups[i][j].clear();
}
}
}
/*********************************************************/
/*** BATTLEGROUND QUEUE SELECTION POOLS ***/
/*********************************************************/
// selection pool initialization, used to clean up from prev selection
void BattleGroundQueue::SelectionPool::Init()
{
SelectedGroups.clear();
PlayerCount = 0;
}
// remove group info from selection pool
// returns true when we need to try to add new group to selection pool
// returns false when selection pool is ok or when we kicked smaller group than we need to kick
// sometimes it can be called on empty selection pool
bool BattleGroundQueue::SelectionPool::KickGroup(uint32 size)
{
//find maxgroup or LAST group with size == size and kick it
bool found = false;
GroupsQueueType::iterator groupToKick = SelectedGroups.begin();
for (GroupsQueueType::iterator itr = groupToKick; itr != SelectedGroups.end(); ++itr)
{
if (abs((int32)((*itr)->Players.size() - size)) <= 1)
{
groupToKick = itr;
found = true;
}
else if (!found && (*itr)->Players.size() >= (*groupToKick)->Players.size())
groupToKick = itr;
}
//if pool is empty, do nothing
if (GetPlayerCount())
{
//update player count
GroupQueueInfo* ginfo = (*groupToKick);
SelectedGroups.erase(groupToKick);
PlayerCount -= ginfo->Players.size();
//return false if we kicked smaller group or there are enough players in selection pool
if (ginfo->Players.size() <= size + 1)
return false;
}
return true;
}
// add group to selection pool
// used when building selection pools
// returns true if we can invite more players, or when we added group to selection pool
// returns false when selection pool is full
bool BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount)
{
//if group is larger than desired count - don't allow to add it to pool
if (!ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size())
{
SelectedGroups.push_back(ginfo);
// increase selected players count
PlayerCount += ginfo->Players.size();
return true;
}
if (PlayerCount < desiredCount)
return true;
return false;
}
/*********************************************************/
/*** BATTLEGROUND QUEUES ***/
/*********************************************************/
// add group or player (grp == NULL) to bg queue with the given leader and bg specifications
GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, Group* grp, BattleGroundTypeId BgTypeId, PvPDifficultyEntry const* backetEntry, uint8 ArenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 arenateamid)
{
BattleGroundBracketId bracketId = backetEntry->GetBracketId();
// create new ginfo
GroupQueueInfo* ginfo = new GroupQueueInfo;
ginfo->BgTypeId = BgTypeId;
ginfo->ArenaType = ArenaType;
ginfo->ArenaTeamId = arenateamid;
ginfo->IsRated = isRated;
ginfo->IsInvitedToBGInstanceGUID = 0;
ginfo->JoinTime = getMSTime();
ginfo->RemoveInviteTime = 0;
ginfo->Team = leader->GetTeam();
ginfo->ArenaTeamRating = arenaRating;
ginfo->OpponentsTeamRating = 0;
ginfo->Players.clear();
//compute index (if group is premade or joined a rated match) to queues
uint32 index = 0;
if (!isRated && !isPremade)
index += BG_TEAMS_COUNT;
if (ginfo->Team == HORDE)
index++;
sLog.outDebug("Adding Group to BattleGroundQueue bgTypeId : %u, bracket_id : %u, index : %u", BgTypeId, bracketId, index);
uint32 lastOnlineTime = getMSTime();
//announce world (this don't need mutex)
if (isRated && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE))
{
ArenaTeam *Team = objmgr.GetArenaTeamById(arenateamid);
if (Team)
sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, Team->GetName().c_str(), ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating);
}
//add players from group to ginfo
{
//ACE_Guard guard(m_Lock);
if (grp)
{
for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *member = itr->getSource();
if (!member)
continue; // this should never happen
PlayerQueueInfo& pl_info = m_QueuedPlayers[member->GetGUID()];
pl_info.LastOnlineTime = lastOnlineTime;
pl_info.GroupInfo = ginfo;
// add the pinfo to ginfo's list
ginfo->Players[member->GetGUID()] = &pl_info;
}
}
else
{
PlayerQueueInfo& pl_info = m_QueuedPlayers[leader->GetGUID()];
pl_info.LastOnlineTime = lastOnlineTime;
pl_info.GroupInfo = ginfo;
ginfo->Players[leader->GetGUID()] = &pl_info;
}
//add GroupInfo to m_QueuedGroups
m_QueuedGroups[bracketId][index].push_back(ginfo);
//announce to world, this code needs mutex
if (!isRated && !isPremade && sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
{
if (BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId))
{
char const* bgName = bg->GetName();
uint32 MinPlayers = bg->GetMinPlayersPerTeam();
uint32 qHorde = 0;
uint32 qAlliance = 0;
uint32 q_min_level = backetEntry->minLevel;
uint32 q_max_level = backetEntry->maxLevel;
GroupsQueueType::const_iterator itr;
for (itr = m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_ALLIANCE].begin(); itr != m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_ALLIANCE].end(); ++itr)
if (!(*itr)->IsInvitedToBGInstanceGUID)
qAlliance += (*itr)->Players.size();
for (itr = m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_HORDE].begin(); itr != m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_HORDE].end(); ++itr)
if (!(*itr)->IsInvitedToBGInstanceGUID)
qHorde += (*itr)->Players.size();
// Show queue status to player only (when joining queue)
if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY))
{
ChatHandler(leader).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, bgName, q_min_level, q_max_level,
qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0);
}
// System message
else
{
sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, bgName, q_min_level, q_max_level,
qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0);
}
}
}
//release mutex
}
return ginfo;
}
void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id)
{
uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, getMSTime());
uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas!
if (!ginfo->ArenaType)
{
if (ginfo->Team == HORDE)
team_index = BG_TEAM_HORDE;
}
else
{
if (ginfo->IsRated)
team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE
}
//store pointer to arrayindex of player that was added first
uint32* lastPlayerAddedPointer = &(m_WaitTimeLastPlayer[team_index][bracket_id]);
//remove his time from sum
m_SumOfWaitTimes[team_index][bracket_id] -= m_WaitTimes[team_index][bracket_id][(*lastPlayerAddedPointer)];
//set average time to new
m_WaitTimes[team_index][bracket_id][(*lastPlayerAddedPointer)] = timeInQueue;
//add new time to sum
m_SumOfWaitTimes[team_index][bracket_id] += timeInQueue;
//set index of last player added to next one
(*lastPlayerAddedPointer)++;
(*lastPlayerAddedPointer) %= COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME;
}
uint32 BattleGroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id)
{
uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas!
if (!ginfo->ArenaType)
{
if (ginfo->Team == HORDE)
team_index = BG_TEAM_HORDE;
}
else
{
if (ginfo->IsRated)
team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE
}
//check if there is enought values(we always add values > 0)
if (m_WaitTimes[team_index][bracket_id][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1])
return (m_SumOfWaitTimes[team_index][bracket_id] / COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME);
else
//if there aren't enough values return 0 - not available
return 0;
}
//remove player from queue and from group info, if group info is empty then remove it too
void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCount)
{
//Player *plr = objmgr.GetPlayer(guid);
int32 bracket_id = -1; // signed for proper for-loop finish
QueuedPlayersMap::iterator itr;
//remove player from map, if he's there
itr = m_QueuedPlayers.find(guid);
if (itr == m_QueuedPlayers.end())
{
sLog.outError("BattleGroundQueue: couldn't find player to remove GUID: %u", GUID_LOPART(guid));
return;
}
GroupQueueInfo* group = itr->second.GroupInfo;
GroupsQueueType::iterator group_itr, group_itr_tmp;
// mostly people with the highest levels are in battlegrounds, thats why
// we count from MAX_BATTLEGROUND_QUEUES - 1 to 0
// variable index removes useless searching in other team's queue
uint32 index = (group->Team == HORDE) ? BG_TEAM_HORDE : BG_TEAM_ALLIANCE;
for (int32 bracket_id_tmp = MAX_BATTLEGROUND_BRACKETS - 1; bracket_id_tmp >= 0 && bracket_id == -1; --bracket_id_tmp)
{
//we must check premade and normal team's queue - because when players from premade are joining bg,
//they leave groupinfo so we can't use its players size to find out index
for (uint32 j = index; j < BG_QUEUE_GROUP_TYPES_COUNT; j += BG_QUEUE_NORMAL_ALLIANCE)
{
for (group_itr_tmp = m_QueuedGroups[bracket_id_tmp][j].begin(); group_itr_tmp != m_QueuedGroups[bracket_id_tmp][j].end(); ++group_itr_tmp)
{
if ((*group_itr_tmp) == group)
{
bracket_id = bracket_id_tmp;
group_itr = group_itr_tmp;
//we must store index to be able to erase iterator
index = j;
break;
}
}
}
}
//player can't be in queue without group, but just in case
if (bracket_id == -1)
{
sLog.outError("BattleGroundQueue: ERROR Cannot find groupinfo for player GUID: %u", GUID_LOPART(guid));
return;
}
sLog.outDebug("BattleGroundQueue: Removing player GUID %u, from bracket_id %u", GUID_LOPART(guid), (uint32)bracket_id);
// ALL variables are correctly set
// We can ignore leveling up in queue - it should not cause crash
// remove player from group
// if only one player there, remove group
// remove player queue info from group queue info
std::map::iterator pitr = group->Players.find(guid);
if (pitr != group->Players.end())
group->Players.erase(pitr);
// if invited to bg, and should decrease invited count, then do it
if (decreaseInvitedCount && group->IsInvitedToBGInstanceGUID)
{
BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID, group->BgTypeId);
if (bg)
bg->DecreaseInvitedCount(group->Team);
}
// remove player queue info
m_QueuedPlayers.erase(itr);
// announce to world if arena team left queue for rated match, show only once
if (group->ArenaType && group->IsRated && group->Players.empty() && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE))
{
ArenaTeam *Team = objmgr.GetArenaTeamById(group->ArenaTeamId);
if (Team)
sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, Team->GetName().c_str(), group->ArenaType, group->ArenaType, group->ArenaTeamRating);
}
//if player leaves queue and he is invited to rated arena match, then he have to loose
if (group->IsInvitedToBGInstanceGUID && group->IsRated && decreaseInvitedCount)
{
ArenaTeam * at = objmgr.GetArenaTeamById(group->ArenaTeamId);
if (at)
{
sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(guid), group->OpponentsTeamRating);
Player *plr = objmgr.GetPlayer(guid);
if (plr)
at->MemberLost(plr, group->OpponentsTeamRating);
else
at->OfflineMemberLost(guid, group->OpponentsTeamRating);
at->SaveToDB();
}
}
// remove group queue info if needed
if (group->Players.empty())
{
m_QueuedGroups[bracket_id][index].erase(group_itr);
delete group;
}
// if group wasn't empty, so it wasn't deleted, and player have left a rated
// queue -> everyone from the group should leave too
// don't remove recursively if already invited to bg!
else if (!group->IsInvitedToBGInstanceGUID && group->IsRated)
{
// remove next player, this is recursive
// first send removal information
if (Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first))
{
BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId);
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(group->BgTypeId, group->ArenaType);
uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId);
plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to
// queue->removeplayer, it causes bugs
WorldPacket data;
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0);
plr2->GetSession()->SendPacket(&data);
}
// then actually delete, this may delete the group as well!
RemovePlayer(group->Players.begin()->first, decreaseInvitedCount);
}
}
//returns true when player pl_guid is in queue and is invited to bgInstanceGuid
bool BattleGroundQueue::IsPlayerInvited(const uint64& pl_guid, const uint32 bgInstanceGuid, const uint32 removeTime)
{
QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(pl_guid);
return (qItr != m_QueuedPlayers.end()
&& qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == bgInstanceGuid
&& qItr->second.GroupInfo->RemoveInviteTime == removeTime);
}
bool BattleGroundQueue::GetPlayerGroupInfoData(const uint64& guid, GroupQueueInfo* ginfo)
{
QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(guid);
if (qItr == m_QueuedPlayers.end())
return false;
*ginfo = *(qItr->second.GroupInfo);
return true;
}
bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side)
{
// set side if needed
if (side)
ginfo->Team = side;
if (!ginfo->IsInvitedToBGInstanceGUID)
{
// not yet invited
// set invitation
ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID();
BattleGroundTypeId bgTypeId = bg->GetTypeID();
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, bg->GetArenaType());
BattleGroundBracketId bracket_id = bg->GetBracketId();
// set ArenaTeamId for rated matches
if (bg->isArena() && bg->isRated())
bg->SetArenaTeamIdForTeam(ginfo->Team, ginfo->ArenaTeamId);
ginfo->RemoveInviteTime = getMSTime() + INVITE_ACCEPT_WAIT_TIME;
// loop through the players
for (std::map::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr)
{
// get the player
Player* plr = objmgr.GetPlayer(itr->first);
// if offline, skip him, this should not happen - player is removed from queue when he logs out
if (!plr)
continue;
// invite the player
PlayerInvitedToBGUpdateAverageWaitTime(ginfo, bracket_id);
//sBattleGroundMgr.InvitePlayer(plr, bg, ginfo->Team);
// set invited player counters
bg->IncreaseInvitedCount(ginfo->Team);
plr->SetInviteForBattleGroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID);
// create remind invite events
BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->ArenaType, ginfo->RemoveInviteTime);
plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITATION_REMIND_TIME));
// create automatic remove events
BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime);
plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME));
WorldPacket data;
uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID());
// send status packet
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType);
plr->GetSession()->SendPacket(&data);
}
return true;
}
return false;
}
/*
This function is inviting players to already running battlegrounds
Invitation type is based on config file
large groups are disadvantageous, because they will be kicked first if invitation type = 1
*/
void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BattleGroundBracketId bracket_id)
{
int32 hordeFree = bg->GetFreeSlotsForTeam(HORDE);
int32 aliFree = bg->GetFreeSlotsForTeam(ALLIANCE);
//iterator for iterating through bg queue
GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].begin();
//count of groups in queue - used to stop cycles
uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size();
//index to queue which group is current
uint32 aliIndex = 0;
for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); aliIndex++)
++Ali_itr;
//the same thing for horde
GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].begin();
uint32 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size();
uint32 hordeIndex = 0;
for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); hordeIndex++)
++Horde_itr;
//if ofc like BG queue invitation is set in config, then we are happy
if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0)
return;
/*
if we reached this code, then we have to solve NP - complete problem called Subset sum problem
So one solution is to check all possible invitation subgroups, or we can use these conditions:
1. Last time when BattleGroundQueue::Update was executed we invited all possible players - so there is only small possibility
that we will invite now whole queue, because only 1 change has been made to queues from the last BattleGroundQueue::Update call
2. Other thing we should consider is group order in queue
*/
// At first we need to compare free space in bg and our selection pool
int32 diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount());
int32 diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount());
while (abs(diffAli - diffHorde) > 1 && (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() > 0))
{
//each cycle execution we need to kick at least 1 group
if (diffAli < diffHorde)
{
//kick alliance group, add to pool new group if needed
if (m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffHorde - diffAli))
{
for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), (aliFree >= diffHorde) ? aliFree - diffHorde : 0); aliIndex++)
++Ali_itr;
}
//if ali selection is already empty, then kick horde group, but if there are less horde than ali in bg - break;
if (!m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())
{
if (aliFree <= diffHorde + 1)
break;
m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffHorde - diffAli);
}
}
else
{
//kick horde group, add to pool new group if needed
if (m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffAli - diffHorde))
{
for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++)
++Horde_itr;
}
if (!m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount())
{
if (hordeFree <= diffAli + 1)
break;
m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde);
}
}
//count diffs after small update
diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount());
diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount());
}
}
// this method checks if premade versus premade battleground is possible
// then after 30 mins (default) in queue it moves premade group to normal queue
// it tries to invite as much players as it can - to MaxPlayersPerTeam, because premade groups have more than MinPlayersPerTeam players
bool BattleGroundQueue::CheckPremadeMatch(BattleGroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam)
{
//check match
if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && !m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty())
{
//start premade match
//if groups aren't invited
GroupsQueueType::const_iterator ali_group, horde_group;
for (ali_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++ali_group)
if (!(*ali_group)->IsInvitedToBGInstanceGUID)
break;
for (horde_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++horde_group)
if (!(*horde_group)->IsInvitedToBGInstanceGUID)
break;
if (ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end())
{
m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam);
m_SelectionPools[BG_TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam);
//add groups/players from normal queue to size of bigger group
uint32 maxPlayers = std::min(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount());
GroupsQueueType::const_iterator itr;
for (uint32 i = 0; i < BG_TEAMS_COUNT; i++)
{
for (itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr)
{
//if itr can join BG and player count is less that maxPlayers, then add group to selectionpool
if (!(*itr)->IsInvitedToBGInstanceGUID && !m_SelectionPools[i].AddGroup((*itr), maxPlayers))
break;
}
}
//premade selection pools are set
return true;
}
}
// now check if we can move group from Premade queue to normal queue (timer has expired) or group size lowered!!
// this could be 2 cycles but i'm checking only first team in queue - it can cause problem -
// if first is invited to BG and seconds timer expired, but we can ignore it, because players have only 80 seconds to click to enter bg
// and when they click or after 80 seconds the queue info is removed from queue
uint32 time_before = getMSTime() - sWorld.getConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH);
for (uint32 i = 0; i < BG_TEAMS_COUNT; i++)
{
if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].empty())
{
GroupsQueueType::iterator itr = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].begin();
if (!(*itr)->IsInvitedToBGInstanceGUID && ((*itr)->JoinTime < time_before || (*itr)->Players.size() < MinPlayersPerTeam))
{
//we must insert group to normal queue and erase pointer from premade queue
m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr));
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr);
}
}
}
//selection pools are not set
return false;
}
// this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam
bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers)
{
GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT];
for (uint32 i = 0; i < BG_TEAMS_COUNT; i++)
{
itr_team[i] = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin();
for (; itr_team[i] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++(itr_team[i]))
{
if (!(*(itr_team[i]))->IsInvitedToBGInstanceGUID)
{
m_SelectionPools[i].AddGroup(*(itr_team[i]), maxPlayers);
if (m_SelectionPools[i].GetPlayerCount() >= minPlayers)
break;
}
}
}
//try to invite same number of players - this cycle may cause longer wait time even if there are enough players in queue, but we want ballanced bg
uint32 j = BG_TEAM_ALLIANCE;
if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())
j = BG_TEAM_HORDE;
if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != 0
&& m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers)
{
//we will try to invite more groups to team with less players indexed by j
++(itr_team[j]); //this will not cause a crash, because for cycle above reached break;
for (; itr_team[j] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + j].end(); ++(itr_team[j]))
{
if (!(*(itr_team[j]))->IsInvitedToBGInstanceGUID)
if (!m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % BG_TEAMS_COUNT].GetPlayerCount()))
break;
}
// do not allow to start bg with more than 2 players more on 1 faction
if (abs((int32)(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())) > 2)
return false;
}
//allow 1v0 if debug bg
if (sBattleGroundMgr.isTesting() && bg_template->isBattleGround() && (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()))
return true;
//return true if there are enough players in selection pools - enable to work .debug bg command correctly
return m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers;
}
// this method will check if we can invite players to same faction skirmish match
bool BattleGroundQueue::CheckSkirmishForSameFaction(BattleGroundBracketId bracket_id, uint32 minPlayersPerTeam)
{
if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam)
return false;
uint32 teamIndex = BG_TEAM_ALLIANCE;
uint32 otherTeam = BG_TEAM_HORDE;
uint32 otherTeamId = HORDE;
if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam)
{
teamIndex = BG_TEAM_HORDE;
otherTeam = BG_TEAM_ALLIANCE;
otherTeamId = ALLIANCE;
}
//clear other team's selection
m_SelectionPools[otherTeam].Init();
//store last ginfo pointer
GroupQueueInfo* ginfo = m_SelectionPools[teamIndex].SelectedGroups.back();
//set itr_team to group that was added to selection pool latest
GroupsQueueType::iterator itr_team = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].begin();
for (; itr_team != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team)
if (ginfo == *itr_team)
break;
if (itr_team == m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end())
return false;
GroupsQueueType::iterator itr_team2 = itr_team;
++itr_team2;
//invite players to other selection pool
for (; itr_team2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team2)
{
//if selection pool is full then break;
if (!(*itr_team2)->IsInvitedToBGInstanceGUID && !m_SelectionPools[otherTeam].AddGroup(*itr_team2, minPlayersPerTeam))
break;
}
if (m_SelectionPools[otherTeam].GetPlayerCount() != minPlayersPerTeam)
return false;
//here we have correct 2 selections and we need to change one teams team and move selection pool teams to other team's queue
for (GroupsQueueType::iterator itr = m_SelectionPools[otherTeam].SelectedGroups.begin(); itr != m_SelectionPools[otherTeam].SelectedGroups.end(); ++itr)
{
//set correct team
(*itr)->Team = otherTeamId;
//add team to other queue
m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + otherTeam].push_front(*itr);
//remove team from old queue
GroupsQueueType::iterator itr2 = itr_team;
++itr2;
for (; itr2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr2)
{
if (*itr2 == *itr)
{
m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].erase(itr2);
break;
}
}
}
return true;
}
/*
this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while (true) cycles to invite whole queue
it must be called after fully adding the members of a group to ensure group joining
should be called from BattleGround::RemovePlayer function in some cases
*/
void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating)
{
//if no players in queue - do nothing
if (m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() &&
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty() &&
m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].empty() &&
m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].empty())
return;
//battleground with free slot for player should be always in the beggining of the queue
// maybe it would be better to create bgfreeslotqueue for each bracket_id
BGFreeSlotQueueType::iterator itr, next;
for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next)
{
next = itr;
++next;
// DO NOT allow queue manager to invite new player to arena
if ((*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetBracketId() == bracket_id &&
(*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE)
{
BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!)
// and iterator is invalid
// clear selection pools
m_SelectionPools[BG_TEAM_ALLIANCE].Init();
m_SelectionPools[BG_TEAM_HORDE].Init();
// call a function that does the job for us
FillPlayersToBG(bg, bracket_id);
// now everything is set, invite players
for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++citr)
InviteGroupToBG((*citr), bg, (*citr)->Team);
for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++citr)
InviteGroupToBG((*citr), bg, (*citr)->Team);
if (!bg->HasFreeSlots())
{
// remove BG from BGFreeSlotQueue
bg->RemoveFromBGFreeSlotQueue();
}
}
}
// finished iterating through the bgs with free slots, maybe we need to create a new bg
BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
if (!bg_template)
{
sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId);
return;
}
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketById(bg_template->GetMapId(),bracket_id);
if (!bracketEntry)
{
sLog.outError("Battleground: Update: bg bracket entry not found for map %u bracket id %u", bg_template->GetMapId(), bracket_id);
return;
}
// get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!)
uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam();
uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam();
if (sBattleGroundMgr.isTesting())
MinPlayersPerTeam = 1;
if (bg_template->isArena())
{
if (sBattleGroundMgr.isArenaTesting())
{
MaxPlayersPerTeam = 1;
MinPlayersPerTeam = 1;
}
else
{
//this switch can be much shorter
MaxPlayersPerTeam = arenaType;
MinPlayersPerTeam = arenaType;
/*switch(arenaType)
{
case ARENA_TYPE_2v2:
MaxPlayersPerTeam = 2;
MinPlayersPerTeam = 2;
break;
case ARENA_TYPE_3v3:
MaxPlayersPerTeam = 3;
MinPlayersPerTeam = 3;
break;
case ARENA_TYPE_5v5:
MaxPlayersPerTeam = 5;
MinPlayersPerTeam = 5;
break;
}*/
}
}
m_SelectionPools[BG_TEAM_ALLIANCE].Init();
m_SelectionPools[BG_TEAM_HORDE].Init();
if (bg_template->isBattleGround())
{
//check if there is premade against premade match
if (CheckPremadeMatch(bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam))
{
//create new battleground
BattleGround * bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, 0, false);
if (!bg2)
{
sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId);
return;
}
//invite those selection pools
for (uint32 i = 0; i < BG_TEAMS_COUNT; i++)
for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr)
InviteGroupToBG((*citr), bg2, (*citr)->Team);
//start bg
bg2->StartBattleGround();
//clear structures
m_SelectionPools[BG_TEAM_ALLIANCE].Init();
m_SelectionPools[BG_TEAM_HORDE].Init();
}
}
// now check if there are in queues enough players to start new game of (normal battleground, or non-rated arena)
if (!isRated)
{
// if there are enough players in pools, start new battleground or non rated arena
if (CheckNormalMatch(bg_template, bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam)
|| (bg_template->isArena() && CheckSkirmishForSameFaction(bracket_id, MinPlayersPerTeam)))
{
// we successfully created a pool
BattleGround * bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, arenaType, false);
if (!bg2)
{
sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId);
return;
}
// invite those selection pools
for (uint32 i = 0; i < BG_TEAMS_COUNT; i++)
for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr)
InviteGroupToBG((*citr), bg2, (*citr)->Team);
// start bg
bg2->StartBattleGround();
}
}
else if (bg_template->isArena())
{
// found out the minimum and maximum ratings the newly added team should battle against
// arenaRating is the rating of the latest joined team, or 0
// 0 is on (automatic update call) and we must set it to team's with longest wait time
if (!arenaRating)
{
GroupQueueInfo* front1 = NULL;
GroupQueueInfo* front2 = NULL;
if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty())
{
front1 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].front();
arenaRating = front1->ArenaTeamRating;
}
if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty())
{
front2 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].front();
arenaRating = front2->ArenaTeamRating;
}
if (front1 && front2)
{
if (front1->JoinTime < front2->JoinTime)
arenaRating = front1->ArenaTeamRating;
}
else if (!front1 && !front2)
return; //queues are empty
}
//set rating range
uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference();
uint32 arenaMaxRating = arenaRating + sBattleGroundMgr.GetMaxRatingDifference();
// if max rating difference is set and the time past since server startup is greater than the rating discard time
// (after what time the ratings aren't taken into account when making teams) then
// the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account
// else leave the discard time on 0, this way all ratings will be discarded
uint32 discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer();
// we need to find 2 teams which will play next game
GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT];
//optimalization : --- we dont need to use selection_pools - each update we select max 2 groups
for (uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++)
{
// take the group that joined first
itr_team[i] = m_QueuedGroups[bracket_id][i].begin();
for (; itr_team[i] != m_QueuedGroups[bracket_id][i].end(); ++(itr_team[i]))
{
// if group match conditions, then add it to pool
if (!(*itr_team[i])->IsInvitedToBGInstanceGUID
&& (((*itr_team[i])->ArenaTeamRating >= arenaMinRating && (*itr_team[i])->ArenaTeamRating <= arenaMaxRating)
|| (*itr_team[i])->JoinTime < discardTime))
{
m_SelectionPools[i].AddGroup((*itr_team[i]), MaxPlayersPerTeam);
// break for cycle to be able to start selecting another group from same faction queue
break;
}
}
}
// now we are done if we have 2 groups - ali vs horde!
// if we don't have, we must try to continue search in same queue
// tmp variables are correctly set
// this code isn't much userfriendly - but it is supposed to continue search for mathing group in HORDE queue
if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount())
{
itr_team[BG_TEAM_ALLIANCE] = itr_team[BG_TEAM_HORDE];
++itr_team[BG_TEAM_ALLIANCE];
for (; itr_team[BG_TEAM_ALLIANCE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[BG_TEAM_ALLIANCE]))
{
if (!(*itr_team[BG_TEAM_ALLIANCE])->IsInvitedToBGInstanceGUID
&& (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating)
|| (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime))
{
m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam);
break;
}
}
}
// this code isn't much userfriendly - but it is supposed to continue search for mathing group in ALLIANCE queue
if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())
{
itr_team[BG_TEAM_HORDE] = itr_team[BG_TEAM_ALLIANCE];
++itr_team[BG_TEAM_HORDE];
for (; itr_team[BG_TEAM_HORDE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[BG_TEAM_HORDE]))
{
if (!(*itr_team[BG_TEAM_HORDE])->IsInvitedToBGInstanceGUID
&& (((*itr_team[BG_TEAM_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_HORDE])->ArenaTeamRating <= arenaMaxRating)
|| (*itr_team[BG_TEAM_HORDE])->JoinTime < discardTime))
{
m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam);
break;
}
}
}
//if we have 2 teams, then start new arena and invite players!
if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount())
{
BattleGround* arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, arenaType, true);
if (!arena)
{
sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!");
return;
}
(*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating;
sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating);
(*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating;
sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamId, (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating);
// now we must move team if we changed its faction to another faction queue, because then we will spam log by errors in Queue::RemovePlayer
if ((*(itr_team[BG_TEAM_ALLIANCE]))->Team != ALLIANCE)
{
// add to alliance queue
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[BG_TEAM_ALLIANCE]));
// erase from horde queue
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[BG_TEAM_ALLIANCE]);
itr_team[BG_TEAM_ALLIANCE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin();
}
if ((*(itr_team[BG_TEAM_HORDE]))->Team != HORDE)
{
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[BG_TEAM_HORDE]));
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]);
itr_team[BG_TEAM_HORDE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin();
}
InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE);
InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE);
sLog.outDebug("Starting rated arena match!");
arena->StartBattleGround();
}
}
}
/*********************************************************/
/*** BATTLEGROUND QUEUE EVENTS ***/
/*********************************************************/
bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
Player* plr = objmgr.GetPlayer(m_PlayerGuid);
// player logged off (we should do nothing, he is correctly removed from queue in another procedure)
if (!plr)
return true;
BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId);
//if battleground ended and its instance deleted - do nothing
if (!bg)
return true;
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue or in battleground
{
// check if player is invited to this bg
BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId];
if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime))
{
WorldPacket data;
//we must send remaining time in queue
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, 0, m_ArenaType);
plr->GetSession()->SendPacket(&data);
}
}
return true; //event will be deleted
}
void BGQueueInviteEvent::Abort(uint64 /*e_time*/)
{
//do nothing
}
/*
this event has many possibilities when it is executed:
1. player is in battleground (he clicked enter on invitation window)
2. player left battleground queue and he isn't there any more
3. player left battleground queue and he joined it again and IsInvitedToBGInstanceGUID = 0
4. player left queue and he joined again and he has been invited to same battleground again -> we should not remove him from queue yet
5. player is invited to bg and he didn't choose what to do and timer expired - only in this condition we should call queue::RemovePlayer
we must remove player in the 5. case even if battleground object doesn't exist!
*/
bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
Player* plr = objmgr.GetPlayer(m_PlayerGuid);
if (!plr)
// player logged off (we should do nothing, he is correctly removed from queue in another procedure)
return true;
BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId);
//battleground can be deleted already when we are removing queue info
//bg pointer can be NULL! so use it carefully!
uint32 queueSlot = plr->GetBattleGroundQueueIndex(m_BgQueueTypeId);
if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue, or in Battleground
{
// check if player is in queue for this BG and if we are removing his invite event
BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId];
if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime))
{
sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID);
plr->RemoveBattleGroundQueueId(m_BgQueueTypeId);
bgQueue.RemovePlayer(m_PlayerGuid, true);
//update queues if battleground isn't ended
if (bg && bg->isBattleGround() && bg->GetStatus() != STATUS_WAIT_LEAVE)
sBattleGroundMgr.ScheduleQueueUpdate(0, 0, m_BgQueueTypeId, m_BgTypeId, bg->GetBracketId());
WorldPacket data;
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0);
plr->GetSession()->SendPacket(&data);
}
}
//event will be deleted
return true;
}
void BGQueueRemoveEvent::Abort(uint64 /*e_time*/)
{
//do nothing
}
/*********************************************************/
/*** BATTLEGROUND MANAGER ***/
/*********************************************************/
BattleGroundMgr::BattleGroundMgr() : m_AutoDistributionTimeChecker(0), m_ArenaTesting(false)
{
for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++)
m_BattleGrounds[i].clear();
m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER);
m_Testing=false;
}
BattleGroundMgr::~BattleGroundMgr()
{
DeleteAllBattleGrounds();
}
void BattleGroundMgr::DeleteAllBattleGrounds()
{
for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; ++i)
{
for (BattleGroundSet::iterator itr = m_BattleGrounds[i].begin(); itr != m_BattleGrounds[i].end();)
{
BattleGround * bg = itr->second;
m_BattleGrounds[i].erase(itr++);
if (!m_ClientBattleGroundIds[i][bg->GetBracketId()].empty())
m_ClientBattleGroundIds[i][bg->GetBracketId()].erase(bg->GetClientInstanceID());
delete bg;
}
}
// destroy template battlegrounds that listed only in queues (other already terminated)
for (uint32 bgTypeId = 0; bgTypeId < MAX_BATTLEGROUND_TYPE_ID; ++bgTypeId)
{
// ~BattleGround call unregistring BG from queue
while (!BGFreeSlotQueue[bgTypeId].empty())
delete BGFreeSlotQueue[bgTypeId].front();
}
}
// used to update running battlegrounds, and delete finished ones
void BattleGroundMgr::Update(uint32 diff)
{
BattleGroundSet::iterator itr, next;
for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; ++i)
{
itr = m_BattleGrounds[i].begin();
// skip updating battleground template
if (itr != m_BattleGrounds[i].end())
++itr;
for (; itr != m_BattleGrounds[i].end(); itr = next)
{
next = itr;
++next;
itr->second->Update(diff);
// use the SetDeleteThis variable
// direct deletion caused crashes
if (itr->second->m_SetDeleteThis)
{
BattleGround * bg = itr->second;
m_BattleGrounds[i].erase(itr);
if (!m_ClientBattleGroundIds[i][bg->GetBracketId()].empty())
m_ClientBattleGroundIds[i][bg->GetBracketId()].erase(bg->GetClientInstanceID());
delete bg;
}
}
}
// update scheduled queues
if (!m_QueueUpdateScheduler.empty())
{
std::vector scheduled;
{
//copy vector and clear the other
scheduled = std::vector(m_QueueUpdateScheduler);
m_QueueUpdateScheduler.clear();
//release lock
}
for (uint8 i = 0; i < scheduled.size(); i++)
{
uint32 arenaRating = scheduled[i] >> 32;
uint8 arenaType = scheduled[i] >> 24 & 255;
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundQueueTypeId(scheduled[i] >> 16 & 255);
BattleGroundTypeId bgTypeId = BattleGroundTypeId((scheduled[i] >> 8) & 255);
BattleGroundBracketId bracket_id = BattleGroundBracketId(scheduled[i] & 255);
m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, bracket_id, arenaType, arenaRating > 0, arenaRating);
}
}
// if rating difference counts, maybe force-update queues
if (sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE) && sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER))
{
// it's time to force update
if (m_NextRatingDiscardUpdate < diff)
{
// forced update for rated arenas (scan all, but skipped non rated)
sLog.outDebug("BattleGroundMgr: UPDATING ARENA QUEUES");
for (int qtype = BATTLEGROUND_QUEUE_2v2; qtype <= BATTLEGROUND_QUEUE_5v5; ++qtype)
for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
m_BattleGroundQueues[qtype].Update(
BATTLEGROUND_AA, BattleGroundBracketId(bracket),
BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId(qtype)), true, 0);
m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER);
}
else
m_NextRatingDiscardUpdate -= diff;
}
if (sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS))
{
if (m_AutoDistributionTimeChecker < diff)
{
if (time(NULL) > m_NextAutoDistributionTime)
{
DistributeArenaPoints();
m_NextAutoDistributionTime = time(NULL) + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS);
sWorld.setWorldState(WS_ARENA_DISTRIBUTION_TIME, uint64(m_NextAutoDistributionTime));
}
m_AutoDistributionTimeChecker = 600000; // check 10 minutes
}
else
m_AutoDistributionTimeChecker -= diff;
}
}
void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype)
{
// we can be in 2 queues in same time...
if (StatusID == 0 || !bg)
{
data->Initialize(SMSG_BATTLEFIELD_STATUS, 4+8);
*data << uint32(QueueSlot); // queue id (0...1)
*data << uint64(0);
return;
}
data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+8+1+1+4+1+4+4+4));
*data << uint32(QueueSlot); // queue id (0...1) - player can be in 2 queues in time
// The following segment is read as uint64 in client but can be appended as their original type.
*data << uint8(arenatype);
sLog.outDebug("BattleGroundMgr::BuildBattleGroundStatusPacket: arenatype = %u for bg instanceID %u, TypeID %u.", arenatype, bg->GetClientInstanceID(), bg->GetTypeID());
*data << uint8(bg->isArena() ? 0xC : 0x2);
*data << uint32(bg->GetTypeID());
*data << uint16(0x1F90);
// End of uint64 segment, decomposed this way for simplicity
*data << uint8(0); // 3.3.0
*data << uint8(0); // 3.3.0
*data << uint32(bg->GetClientInstanceID());
// alliance/horde for BG and skirmish/rated for Arenas
// following displays the minimap-icon 0 = faction icon 1 = arenaicon
*data << uint8(bg->isRated()); // 1 for rated match, 0 for bg or non rated match
*data << uint32(StatusID); // status
switch(StatusID)
{
case STATUS_WAIT_QUEUE: // status_in_queue
*data << uint32(Time1); // average wait time, milliseconds
*data << uint32(Time2); // time in queue, updated every minute!, milliseconds
break;
case STATUS_WAIT_JOIN: // status_invite
*data << uint32(bg->GetMapId()); // map id
*data << uint32(Time1); // time to remove from queue, milliseconds
break;
case STATUS_IN_PROGRESS: // status_in_progress
*data << uint32(bg->GetMapId()); // map id
*data << uint32(Time1); // time to bg auto leave, 0 at bg start, 120000 after bg end, milliseconds
*data << uint32(Time2); // time from bg start, milliseconds
*data << uint8(/*bg->isArena() ? 0 :*/ 1); // unk, possibly 0 == preparation phase, 1 == battle
break;
default:
sLog.outError("Unknown BG status!");
break;
}
}
void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg)
{
uint8 type = (bg->isArena() ? 1 : 0);
// last check on 3.0.3
data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize()));
*data << uint8(type); // type (battleground=0/arena=1)
if (type) // arena
{
// it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H
for (int i = 1; i >= 0; --i)
{
uint32 pointsLost = bg->m_ArenaTeamRatingChanges[i] < 0 ? abs(bg->m_ArenaTeamRatingChanges[i]) : 0;
uint32 pointsGained = bg->m_ArenaTeamRatingChanges[i] > 0 ? bg->m_ArenaTeamRatingChanges[i] : 0;
*data << uint32(pointsLost); // Rating Lost
*data << uint32(pointsGained); // Rating gained
*data << uint32(0); // Matchmaking Value
sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]);
}
for (int i = 1; i >= 0; --i)
{
uint32 at_id = bg->m_ArenaTeamIds[i];
ArenaTeam * at = objmgr.GetArenaTeamById(at_id);
if (at)
*data << at->GetName();
else
*data << (uint8)0;
}
}
if (bg->GetStatus() != STATUS_WAIT_LEAVE)
{
*data << uint8(0); // bg not ended
}
else
{
*data << uint8(1); // bg ended
*data << uint8(bg->GetWinner()); // who win
}
*data << (int32)(bg->GetPlayerScoresSize());
for (BattleGround::BattleGroundScoreMap::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr)
{
*data << (uint64)itr->first;
*data << (int32)itr->second->KillingBlows;
if (type == 0)
{
*data << (int32)itr->second->HonorableKills;
*data << (int32)itr->second->Deaths;
*data << (int32)(itr->second->BonusHonor);
}
else
{
Player *plr = objmgr.GetPlayer(itr->first);
uint32 team = bg->GetPlayerTeam(itr->first);
if (!team && plr)
team = plr->GetBGTeam();
*data << uint8(team == ALLIANCE ? 1 : 0); // green or yellow
}
*data << (int32)itr->second->DamageDone; // damage done
*data << (int32)itr->second->HealingDone; // healing done
switch(bg->GetTypeID()) // battleground specific things
{
case BATTLEGROUND_AV:
*data << (uint32)0x00000005; // count of next fields
*data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted; // GraveyardsAssaulted
*data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsDefended; // GraveyardsDefended
*data << (uint32)((BattleGroundAVScore*)itr->second)->TowersAssaulted; // TowersAssaulted
*data << (uint32)((BattleGroundAVScore*)itr->second)->TowersDefended; // TowersDefended
*data << (uint32)((BattleGroundAVScore*)itr->second)->MinesCaptured; // MinesCaptured
break;
case BATTLEGROUND_WS:
*data << (uint32)0x00000002; // count of next fields
*data << (uint32)((BattleGroundWGScore*)itr->second)->FlagCaptures; // flag captures
*data << (uint32)((BattleGroundWGScore*)itr->second)->FlagReturns; // flag returns
break;
case BATTLEGROUND_AB:
*data << (uint32)0x00000002; // count of next fields
*data << (uint32)((BattleGroundABScore*)itr->second)->BasesAssaulted; // bases asssulted
*data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended
break;
case BATTLEGROUND_EY:
*data << (uint32)0x00000001; // count of next fields
*data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures
break;
case BATTLEGROUND_NA:
case BATTLEGROUND_BE:
case BATTLEGROUND_AA:
case BATTLEGROUND_RL:
case BATTLEGROUND_SA:
*data << uint32(2);
*data << uint32(((BattleGroundSAScore*)itr->second)->demolishers_destroyed);
*data << uint32(((BattleGroundSAScore*)itr->second)->gates_destroyed);
break;
case BATTLEGROUND_DS: // wotlk
case BATTLEGROUND_RV: // wotlk
case BATTLEGROUND_IC: // wotlk
case BATTLEGROUND_ABG: // wotlk
*data << (int32)0; // 0
break;
default:
sLog.outDebug("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID());
*data << (int32)0;
break;
}
}
}
void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, BattleGroundTypeId bgTypeId)
{
/*bgTypeId is:
0 - Your group has joined a battleground queue, but you are not eligible
1 - Your group has joined the queue for AV
2 - Your group has joined the queue for WS
3 - Your group has joined the queue for AB
4 - Your group has joined the queue for NA
5 - Your group has joined the queue for BE Arena
6 - Your group has joined the queue for All Arenas
7 - Your group has joined the queue for EotS*/
data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4);
*data << uint32(bgTypeId);
}
void BattleGroundMgr::BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value)
{
data->Initialize(SMSG_UPDATE_WORLD_STATE, 4+4);
*data << uint32(field);
*data << uint32(value);
}
void BattleGroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid)
{
data->Initialize(SMSG_PLAY_SOUND, 4);
*data << uint32(soundid);
}
void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, const uint64& guid)
{
data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8);
*data << uint64(guid);
}
void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr)
{
data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8);
*data << uint64(plr->GetGUID());
}
BattleGround * BattleGroundMgr::GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId)
{
//cause at HandleBattleGroundJoinOpcode the clients sends the instanceid he gets from
//SMSG_BATTLEFIELD_LIST we need to find the battleground with this clientinstance-id
BattleGround* bg = GetBattleGroundTemplate(bgTypeId);
if (!bg)
return NULL;
if (bg->isArena())
return GetBattleGround(instanceId, bgTypeId);
for (BattleGroundSet::iterator itr = m_BattleGrounds[bgTypeId].begin(); itr != m_BattleGrounds[bgTypeId].end(); ++itr)
{
if (itr->second->GetClientInstanceID() == instanceId)
return itr->second;
}
return NULL;
}
BattleGround * BattleGroundMgr::GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId)
{
if (!InstanceID)
return NULL;
//search if needed
BattleGroundSet::iterator itr;
if (bgTypeId == BATTLEGROUND_TYPE_NONE)
{
for (uint32 i = BATTLEGROUND_AV; i < MAX_BATTLEGROUND_TYPE_ID; i++)
{
itr = m_BattleGrounds[i].find(InstanceID);
if (itr != m_BattleGrounds[i].end())
return itr->second;
}
return NULL;
}
itr = m_BattleGrounds[bgTypeId].find(InstanceID);
return ((itr != m_BattleGrounds[bgTypeId].end()) ? itr->second : NULL);
}
BattleGround * BattleGroundMgr::GetBattleGroundTemplate(BattleGroundTypeId bgTypeId)
{
//map is sorted and we can be sure that lowest instance id has only BG template
return m_BattleGrounds[bgTypeId].empty() ? NULL : m_BattleGrounds[bgTypeId].begin()->second;
}
uint32 BattleGroundMgr::CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id)
{
if (IsArenaType(bgTypeId))
return 0; //arenas don't have client-instanceids
// we create here an instanceid, which is just for
// displaying this to the client and without any other use..
// the client-instanceIds are unique for each battleground-type
// the instance-id just needs to be as low as possible, beginning with 1
// the following works, because std::set is default ordered with "<"
// the optimalization would be to use as bitmask std::vector - but that would only make code unreadable
uint32 lastId = 0;
for (std::set::iterator itr = m_ClientBattleGroundIds[bgTypeId][bracket_id].begin(); itr != m_ClientBattleGroundIds[bgTypeId][bracket_id].end();)
{
if ((++lastId) != *itr) //if there is a gap between the ids, we will break..
break;
lastId = *itr;
}
m_ClientBattleGroundIds[bgTypeId][bracket_id].insert(lastId + 1);
return lastId + 1;
}
// create a new battleground that will really be used to play
BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated)
{
// get the template BG
BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId);
if (!bg_template)
{
sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId);
return NULL;
}
//for arenas there is random map used
if (bg_template->isArena())
{
if (!isAnyArenaEnabled()) // it's checked in handler but just to be sure
return NULL;
uint8 size = m_EnabledArenas.size() - 1;
bgTypeId = m_EnabledArenas[urand(0,size)];
bg_template = GetBattleGroundTemplate(bgTypeId);
if (!bg_template)
{
sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId);
return NULL;
}
}
BattleGround *bg = NULL;
// create a copy of the BG template
switch(bgTypeId)
{
case BATTLEGROUND_AV:
bg = new BattleGroundAV(*(BattleGroundAV*)bg_template);
break;
case BATTLEGROUND_WS:
bg = new BattleGroundWS(*(BattleGroundWS*)bg_template);
break;
case BATTLEGROUND_AB:
bg = new BattleGroundAB(*(BattleGroundAB*)bg_template);
break;
case BATTLEGROUND_NA:
bg = new BattleGroundNA(*(BattleGroundNA*)bg_template);
break;
case BATTLEGROUND_BE:
bg = new BattleGroundBE(*(BattleGroundBE*)bg_template);
break;
case BATTLEGROUND_AA:
bg = new BattleGroundAA(*(BattleGroundAA*)bg_template);
break;
case BATTLEGROUND_EY:
bg = new BattleGroundEY(*(BattleGroundEY*)bg_template);
break;
case BATTLEGROUND_RL:
bg = new BattleGroundRL(*(BattleGroundRL*)bg_template);
break;
case BATTLEGROUND_SA:
bg = new BattleGroundSA(*(BattleGroundSA*)bg_template);
break;
case BATTLEGROUND_DS:
bg = new BattleGroundDS(*(BattleGroundDS*)bg_template);
break;
case BATTLEGROUND_RV:
bg = new BattleGroundRV(*(BattleGroundRV*)bg_template);
break;
case BATTLEGROUND_IC:
bg = new BattleGroundIC(*(BattleGroundIC*)bg_template);
break;
case BATTLEGROUND_ABG:
bg = new BattleGroundABG(*(BattleGroundABG*)bg_template);
break;
default:
//error, but it is handled few lines above
return 0;
}
// generate a new instance id
bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id
bg->SetClientInstanceID(CreateClientVisibleInstanceId(bgTypeId, bracketEntry->GetBracketId()));
// reset the new bg (set status to status_wait_queue from status_none)
bg->Reset();
// start the joining of the bg
bg->SetStatus(STATUS_WAIT_JOIN);
bg->SetBracket(bracketEntry);
bg->SetArenaType(arenaType);
bg->SetRated(isRated);
return bg;
}
// used to create the BG templates
uint32 BattleGroundMgr::CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO)
{
// Create the BG
BattleGround *bg = NULL;
switch(bgTypeId)
{
case BATTLEGROUND_AV: bg = new BattleGroundAV; break;
case BATTLEGROUND_WS: bg = new BattleGroundWS; break;
case BATTLEGROUND_AB: bg = new BattleGroundAB; break;
case BATTLEGROUND_NA: bg = new BattleGroundNA; break;
case BATTLEGROUND_BE: bg = new BattleGroundBE; break;
case BATTLEGROUND_AA: bg = new BattleGroundAA; break;
case BATTLEGROUND_EY: bg = new BattleGroundEY; break;
case BATTLEGROUND_RL: bg = new BattleGroundRL; break;
case BATTLEGROUND_SA: bg = new BattleGroundSA; break;
case BATTLEGROUND_DS: bg = new BattleGroundDS; break;
case BATTLEGROUND_RV: bg = new BattleGroundRV; break;
case BATTLEGROUND_IC: bg = new BattleGroundIC; break;
case BATTLEGROUND_ABG: bg = new BattleGroundABG; break;
default:
bg = new BattleGround;
break;
}
bg->SetMapId(MapID);
bg->SetTypeID(bgTypeId);
bg->SetInstanceID(0);
bg->SetArenaorBGType(IsArena);
bg->SetMinPlayersPerTeam(MinPlayersPerTeam);
bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam);
bg->SetMinPlayers(MinPlayersPerTeam * 2);
bg->SetMaxPlayers(MaxPlayersPerTeam * 2);
bg->SetName(BattleGroundName);
bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO);
bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO);
bg->SetLevelRange(LevelMin, LevelMax);
// add bg to update list
AddBattleGround(bg->GetInstanceID(), bg->GetTypeID(), bg);
// return some not-null value, bgTypeId is good enough for me
return bgTypeId;
}
void BattleGroundMgr::CreateInitialBattleGrounds()
{
float AStartLoc[4];
float HStartLoc[4];
uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2;
BattlemasterListEntry const *bl;
WorldSafeLocsEntry const *start;
bool IsArena;
uint32 count = 0;
// 0 1 2 3 4 5 6 7 8
QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO FROM battleground_template WHERE disable = 0");
if (!result)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outErrorDb(">> Loaded 0 battlegrounds. DB table `battleground_template` is empty.");
return;
}
barGoLink bar(result->GetRowCount());
do
{
Field *fields = result->Fetch();
bar.step();
uint32 bgTypeID_ = fields[0].GetUInt32();
// can be overwrite by values from DB
bl = sBattlemasterListStore.LookupEntry(bgTypeID_);
if (!bl)
{
sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeID_);
continue;
}
BattleGroundTypeId bgTypeID = BattleGroundTypeId(bgTypeID_);
IsArena = (bl->type == TYPE_ARENA);
MinPlayersPerTeam = fields[1].GetUInt32();
MaxPlayersPerTeam = fields[2].GetUInt32();
MinLvl = fields[3].GetUInt32();
MaxLvl = fields[4].GetUInt32();
//check values from DB
if (MaxPlayersPerTeam == 0 || MinPlayersPerTeam == 0 || MinPlayersPerTeam > MaxPlayersPerTeam)
{
MinPlayersPerTeam = 0; // by default now expected strong full bg requirement
MaxPlayersPerTeam = 40;
}
if (MinLvl == 0 || MaxLvl == 0 || MinLvl > MaxLvl)
{
//TO-DO: FIX ME
MinLvl = 0;//bl->minlvl;
MaxLvl = 80;//bl->maxlvl;
}
start1 = fields[5].GetUInt32();
start = sWorldSafeLocsStore.LookupEntry(start1);
if (start)
{
AStartLoc[0] = start->x;
AStartLoc[1] = start->y;
AStartLoc[2] = start->z;
AStartLoc[3] = fields[6].GetFloat();
}
else if (bgTypeID == BATTLEGROUND_AA || bgTypeID == BATTLEGROUND_ABG)
{
AStartLoc[0] = 0;
AStartLoc[1] = 0;
AStartLoc[2] = 0;
AStartLoc[3] = fields[6].GetFloat();
}
else
{
sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTypeID, start1);
continue;
}
start2 = fields[7].GetUInt32();
start = sWorldSafeLocsStore.LookupEntry(start2);
if (start)
{
HStartLoc[0] = start->x;
HStartLoc[1] = start->y;
HStartLoc[2] = start->z;
HStartLoc[3] = fields[8].GetFloat();
}
else if (bgTypeID == BATTLEGROUND_AA || bgTypeID == BATTLEGROUND_ABG)
{
HStartLoc[0] = 0;
HStartLoc[1] = 0;
HStartLoc[2] = 0;
HStartLoc[3] = fields[8].GetFloat();
}
else
{
sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTypeID, start2);
continue;
}
//sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl);
if (!CreateBattleGround(bgTypeID, IsArena, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3]))
continue;
if (IsArena && bgTypeID != BATTLEGROUND_AA)
m_EnabledArenas.push_back(bgTypeID);
++count;
} while (result->NextRow());
sLog.outString();
sLog.outString(">> Loaded %u battlegrounds", count);
}
void BattleGroundMgr::InitAutomaticArenaPointDistribution()
{
if (sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS))
{
sLog.outDebug("Initializing Automatic Arena Point Distribution");
uint64 wstime = sWorld.getWorldState(WS_ARENA_DISTRIBUTION_TIME);
if (!wstime)
{
sLog.outDebug("Battleground: Next arena point distribution time not found, reseting it now.");
m_NextAutoDistributionTime = time(NULL) + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS);
sWorld.setWorldState(WS_ARENA_DISTRIBUTION_TIME, uint64(m_NextAutoDistributionTime));
}
else
m_NextAutoDistributionTime = time_t(wstime);
sLog.outDebug("Automatic Arena Point Distribution initialized.");
}
}
void BattleGroundMgr::DistributeArenaPoints()
{
// used to distribute arena points based on last week's stats
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_START);
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_START);
//temporary structure for storing maximum points to add values for all players
std::map PlayerPoints;
//at first update all points for all team members
for (ObjectMgr::ArenaTeamMap::iterator team_itr = objmgr.GetArenaTeamMapBegin(); team_itr != objmgr.GetArenaTeamMapEnd(); ++team_itr)
if (ArenaTeam * at = team_itr->second)
at->UpdateArenaPointsHelper(PlayerPoints);
//cycle that gives points to all players
for (std::map::iterator plr_itr = PlayerPoints.begin(); plr_itr != PlayerPoints.end(); ++plr_itr)
{
//update to database
CharacterDatabase.PExecute("UPDATE characters SET arenaPoints = arenaPoints + '%u' WHERE guid = '%u'", plr_itr->second, plr_itr->first);
//add points to player if online
Player* pl = objmgr.GetPlayer(plr_itr->first);
if (pl)
pl->ModifyArenaPoints(plr_itr->second);
}
PlayerPoints.clear();
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_END);
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_START);
for (ObjectMgr::ArenaTeamMap::iterator titr = objmgr.GetArenaTeamMapBegin(); titr != objmgr.GetArenaTeamMapEnd(); ++titr)
{
if (ArenaTeam * at = titr->second)
{
at->FinishWeek(); // set played this week etc values to 0 in memory, too
at->SaveToDB(); // save changes
at->NotifyStatsChanged(); // notify the players of the changes
}
}
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_END);
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_END);
}
void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player* plr, BattleGroundTypeId bgTypeId, uint8 fromWhere)
{
if (!plr)
return;
data->Initialize(SMSG_BATTLEFIELD_LIST);
*data << uint64(guid); // battlemaster guid
*data << uint8(fromWhere); // from where you joined
*data << uint32(bgTypeId); // battleground id
*data << uint8(0); // unk
*data << uint8(0); // unk
// Rewards
*data << uint8(0); // 3.3.3 hasWin
*data << uint32(0); // 3.3.3 winHonor
*data << uint32(0); // 3.3.3 winArena
*data << uint32(0); // 3.3.3 lossHonor
uint8 isRandom = 0;
*data << uint8(isRandom); // 3.3.3 isRandom
if (isRandom)
{
// Rewards (random)
*data << uint8(0); // 3.3.3 hasWin_Random
*data << uint32(0); // 3.3.3 winHonor_Random
*data << uint32(0); // 3.3.3 winArena_Random
*data << uint32(0); // 3.3.3 lossHonor_Random
}
if (bgTypeId == BATTLEGROUND_AA) // arena
{
*data << uint32(0); // unk (count?)
}
else // battleground
{
size_t count_pos = data->wpos();
uint32 count = 0;
*data << uint32(0); // number of bg instances
if (BattleGround* bgTemplate = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId))
{
// expected bracket entry
if (PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bgTemplate->GetMapId(),plr->getLevel()))
{
BattleGroundBracketId bracketId = bracketEntry->GetBracketId();
for (std::set::iterator itr = m_ClientBattleGroundIds[bgTypeId][bracketId].begin(); itr != m_ClientBattleGroundIds[bgTypeId][bracketId].end();++itr)
{
*data << uint32(*itr);
++count;
}
data->put(count_pos , count);
}
}
}
}
void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId, BattleGroundTypeId bgTypeId)
{
BattleGround *bg = GetBattleGround(instanceId, bgTypeId);
if (bg)
{
uint32 mapid = bg->GetMapId();
float x, y, z, O;
uint32 team = pl->GetBGTeam();
if (team == 0)
team = pl->GetTeam();
bg->GetTeamStartLoc(team, x, y, z, O);
sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O);
pl->TeleportTo(mapid, x, y, z, O);
}
else
{
sLog.outError("player %u trying to port to non-existent bg instance %u",pl->GetGUIDLow(), instanceId);
}
}
void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid)
{
WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12);
uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds
if (time_ == uint32(-1))
time_ = 0;
data << guid << time_;
pl->GetSession()->SendPacket(&data);
}
bool BattleGroundMgr::IsArenaType(BattleGroundTypeId bgTypeId)
{
return (bgTypeId == BATTLEGROUND_AA ||
bgTypeId == BATTLEGROUND_BE ||
bgTypeId == BATTLEGROUND_NA ||
bgTypeId == BATTLEGROUND_DS ||
bgTypeId == BATTLEGROUND_RV ||
bgTypeId == BATTLEGROUND_RL ||
bgTypeId == BATTLEGROUND_DS);
}
BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType)
{
switch(bgTypeId)
{
case BATTLEGROUND_WS:
return BATTLEGROUND_QUEUE_WS;
case BATTLEGROUND_AB:
return BATTLEGROUND_QUEUE_AB;
case BATTLEGROUND_AV:
return BATTLEGROUND_QUEUE_AV;
case BATTLEGROUND_EY:
return BATTLEGROUND_QUEUE_EY;
case BATTLEGROUND_SA:
return BATTLEGROUND_QUEUE_SA;
case BATTLEGROUND_IC:
return BATTLEGROUND_QUEUE_IC;
case BATTLEGROUND_ABG:
return BATTLEGROUND_QUEUE_NONE;
case BATTLEGROUND_AA:
case BATTLEGROUND_NA:
case BATTLEGROUND_RL:
case BATTLEGROUND_BE:
case BATTLEGROUND_DS:
case BATTLEGROUND_RV:
switch(arenaType)
{
case ARENA_TYPE_2v2:
return BATTLEGROUND_QUEUE_2v2;
case ARENA_TYPE_3v3:
return BATTLEGROUND_QUEUE_3v3;
case ARENA_TYPE_5v5:
return BATTLEGROUND_QUEUE_5v5;
default:
return BATTLEGROUND_QUEUE_NONE;
}
default:
return BATTLEGROUND_QUEUE_NONE;
}
}
BattleGroundTypeId BattleGroundMgr::BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId)
{
switch(bgQueueTypeId)
{
case BATTLEGROUND_QUEUE_WS:
return BATTLEGROUND_WS;
case BATTLEGROUND_QUEUE_AB:
return BATTLEGROUND_AB;
case BATTLEGROUND_QUEUE_AV:
return BATTLEGROUND_AV;
case BATTLEGROUND_QUEUE_EY:
return BATTLEGROUND_EY;
case BATTLEGROUND_QUEUE_SA:
return BATTLEGROUND_SA;
case BATTLEGROUND_QUEUE_IC:
return BATTLEGROUND_IC;
case BATTLEGROUND_QUEUE_2v2:
case BATTLEGROUND_QUEUE_3v3:
case BATTLEGROUND_QUEUE_5v5:
return BATTLEGROUND_AA;
default:
return BattleGroundTypeId(0); // used for unknown template (it existed and do nothing)
}
}
uint8 BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId bgQueueTypeId)
{
switch(bgQueueTypeId)
{
case BATTLEGROUND_QUEUE_2v2:
return ARENA_TYPE_2v2;
case BATTLEGROUND_QUEUE_3v3:
return ARENA_TYPE_3v3;
case BATTLEGROUND_QUEUE_5v5:
return ARENA_TYPE_5v5;
default:
return 0;
}
}
void BattleGroundMgr::ToggleTesting()
{
m_Testing = !m_Testing;
if (m_Testing)
sWorld.SendWorldText(LANG_DEBUG_BG_ON);
else
sWorld.SendWorldText(LANG_DEBUG_BG_OFF);
}
void BattleGroundMgr::ToggleArenaTesting()
{
m_ArenaTesting = !m_ArenaTesting;
if (m_ArenaTesting)
sWorld.SendWorldText(LANG_DEBUG_ARENA_ON);
else
sWorld.SendWorldText(LANG_DEBUG_ARENA_OFF);
}
void BattleGroundMgr::SetHolidayWeekends(uint32 mask)
{
for (uint32 bgtype = 1; bgtype < MAX_BATTLEGROUND_TYPE_ID; ++bgtype)
{
if (BattleGround * bg = GetBattleGroundTemplate(BattleGroundTypeId(bgtype)))
{
bg->SetHoliday(mask & (1 << bgtype));
}
}
}
void BattleGroundMgr::ScheduleQueueUpdate(uint32 arenaRating, uint8 arenaType, BattleGroundQueueTypeId bgQueueTypeId, BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id)
{
//This method must be atomic, TODO add mutex
//we will use only 1 number created of bgTypeId and bracket_id
uint64 schedule_id = ((uint64)arenaRating << 32) | (arenaType << 24) | (bgQueueTypeId << 16) | (bgTypeId << 8) | bracket_id;
bool found = false;
for (uint8 i = 0; i < m_QueueUpdateScheduler.size(); i++)
{
if (m_QueueUpdateScheduler[i] == schedule_id)
{
found = true;
break;
}
}
if (!found)
m_QueueUpdateScheduler.push_back(schedule_id);
}
uint32 BattleGroundMgr::GetMaxRatingDifference() const
{
// this is for stupid people who can't use brain and set max rating difference to 0
uint32 diff = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE);
if (diff == 0)
diff = 5000;
return diff;
}
uint32 BattleGroundMgr::GetRatingDiscardTimer() const
{
return sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER);
}
uint32 BattleGroundMgr::GetPrematureFinishTime() const
{
return sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER);
}
void BattleGroundMgr::LoadBattleMastersEntry()
{
mBattleMastersMap.clear(); // need for reload case
QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,bg_template FROM battlemaster_entry");
uint32 count = 0;
if (!result)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outString(">> Loaded 0 battlemaster entries - table is empty!");
return;
}
barGoLink bar(result->GetRowCount());
do
{
++count;
bar.step();
Field *fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
uint32 bgTypeId = fields[1].GetUInt32();
if (!sBattlemasterListStore.LookupEntry(bgTypeId))
{
sLog.outErrorDb("Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.",entry,bgTypeId);
continue;
}
mBattleMastersMap[entry] = BattleGroundTypeId(bgTypeId);
} while (result->NextRow());
sLog.outString();
sLog.outString(">> Loaded %u battlemaster entries", count);
}
bool BattleGroundMgr::IsBGWeekend(BattleGroundTypeId bgTypeId)
{
switch (bgTypeId)
{
case BATTLEGROUND_AV:
return IsHolidayActive(HOLIDAY_CALL_TO_ARMS_AV);
case BATTLEGROUND_EY:
return IsHolidayActive(HOLIDAY_CALL_TO_ARMS_EY);
case BATTLEGROUND_WS:
return IsHolidayActive(HOLIDAY_CALL_TO_ARMS_WS);
case BATTLEGROUND_SA:
return IsHolidayActive(HOLIDAY_CALL_TO_ARMS_SA);
default:
return false;
}
}
void BattleGroundMgr::DoCompleteAchievement(uint32 achievement, Player * player)
{
AchievementEntry const* AE = GetAchievementStore()->LookupEntry(achievement);
if (!player)
{
//Map::PlayerList const &PlayerList = this->GetPlayers();
//GroupsQueueType::iterator group = SelectedGroups.begin();
//if (!PlayerList.isEmpty())
//for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
// for (GroupsQueueType::iterator itr = group; itr != SelectedGroups.end(); ++itr)
// if (Player *pPlayer = itr->getSource())
// pPlayer->CompletedAchievement(AE);
}
else
{
player->CompletedAchievement(AE);
}
}