[7338] Little cleanup in battleground queues code.
[getmangos.git] / src / game / BattleGroundMgr.cpp
blobaf56787db28748039121550ddeaca489c6f31434
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Common.h"
20 #include "SharedDefines.h"
21 #include "Player.h"
22 #include "BattleGroundMgr.h"
23 #include "BattleGroundAV.h"
24 #include "BattleGroundAB.h"
25 #include "BattleGroundEY.h"
26 #include "BattleGroundWS.h"
27 #include "BattleGroundNA.h"
28 #include "BattleGroundBE.h"
29 #include "BattleGroundAA.h"
30 #include "BattleGroundRL.h"
31 #include "BattleGroundSA.h"
32 #include "BattleGroundDS.h"
33 #include "BattleGroundRV.h"
34 #include "MapManager.h"
35 #include "Map.h"
36 #include "MapInstanced.h"
37 #include "ObjectMgr.h"
38 #include "ProgressBar.h"
39 #include "Chat.h"
40 #include "ArenaTeam.h"
41 #include "World.h"
42 #include "WorldPacket.h"
43 #include "ProgressBar.h"
45 #include "Policies/SingletonImp.h"
47 INSTANTIATE_SINGLETON_1( BattleGroundMgr );
49 /*********************************************************/
50 /*** BATTLEGROUND QUEUE SYSTEM ***/
51 /*********************************************************/
53 BattleGroundQueue::BattleGroundQueue()
57 BattleGroundQueue::~BattleGroundQueue()
59 m_QueuedPlayers.clear();
60 for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++)
62 for(uint32 j = 0; j < BG_QUEUE_GROUP_TYPES_COUNT; j++)
64 for(GroupsQueueType::iterator itr = m_QueuedGroups[i][j].begin(); itr!= m_QueuedGroups[i][j].end(); ++itr)
65 delete (*itr);
66 m_QueuedGroups[i][j].clear();
71 /*********************************************************/
72 /*** BATTLEGROUND QUEUE SELECTION POOLS ***/
73 /*********************************************************/
75 // selection pool initialization, used to clean up from prev selection
76 void BattleGroundQueue::SelectionPool::Init()
78 SelectedGroups.clear();
79 PlayerCount = 0;
82 // remove group info from selection pool
83 // returns true when we need to try to add new group to selection pool
84 // or false when pool is ok
85 // sometimes it can be called on empty selection pool
86 bool BattleGroundQueue::SelectionPool::KickGroup(uint32 size)
88 //find maxgroup or LAST group with size == size and kick it
89 bool found = false;
90 GroupsQueueType::iterator groupToKick = SelectedGroups.begin();
91 for (GroupsQueueType::iterator itr = groupToKick; itr != SelectedGroups.end(); ++itr)
93 if( abs((int32)((*itr)->Players.size() - size)) <= 1 )
95 groupToKick = itr;
96 found = true;
98 else if (!found && (*itr)->Players.size() >= (*groupToKick)->Players.size())
99 groupToKick = itr;
101 //if pool is empty, do nothing
102 if( GetPlayerCount() )
104 //update player count
105 GroupQueueInfo* ginfo = (*groupToKick);
106 SelectedGroups.erase(groupToKick);
107 PlayerCount -= ginfo->Players.size();
108 if (abs((int32)(ginfo->Players.size() - size)) <= 1)
109 return false;
111 return true;
114 // add group to selection pool
115 // used when building selection pools
116 // returns true if we can invite more players, otherwise return false - (selection pool is set that time)
117 bool BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount)
119 //if group is larger than desired count - don't allow to add it to pool
120 if (!ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size())
122 SelectedGroups.push_back(ginfo);
123 // increase selected players count
124 PlayerCount += ginfo->Players.size();
126 if (PlayerCount == desiredCount)
127 return true;
128 return false;
131 /*********************************************************/
132 /*** BATTLEGROUND QUEUES ***/
133 /*********************************************************/
135 // add group to bg queue with the given leader and bg specifications
136 GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, BattleGroundTypeId BgTypeId, uint8 ArenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 arenateamid)
138 BGQueueIdBasedOnLevel queue_id = leader->GetBattleGroundQueueIdFromLevel(BgTypeId);
140 // create new ginfo
141 // cannot use the method like in addplayer, because that could modify an in-queue group's stats
142 // (e.g. leader leaving queue then joining as individual again)
143 GroupQueueInfo* ginfo = new GroupQueueInfo;
144 ginfo->BgTypeId = BgTypeId;
145 ginfo->ArenaType = ArenaType;
146 ginfo->ArenaTeamId = arenateamid;
147 ginfo->IsRated = isRated;
148 ginfo->IsInvitedToBGInstanceGUID = 0;
149 ginfo->JoinTime = getMSTime();
150 ginfo->Team = leader->GetTeam();
151 ginfo->ArenaTeamRating = arenaRating;
152 ginfo->OpponentsTeamRating = 0;
154 ginfo->Players.clear();
156 //compute index (if group is premade or joined a rated match) to queues
157 uint32 index = 0;
158 if(!isRated && !isPremade)
159 index += BG_TEAMS_COUNT;
160 if(ginfo->Team == HORDE)
161 index++;
162 sLog.outDebug("Adding Group to BattleGroundQueue bgTypeId : %u, queue_id : %u, index : %u", BgTypeId, queue_id, index);
164 m_QueuedGroups[queue_id][index].push_back(ginfo);
166 // return ginfo, because it is needed to add players to this group info
167 return ginfo;
170 //add player to playermap
171 void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo)
173 //if player isn't in queue, he is added, if already is, then values are overwritten, no memory leak
174 PlayerQueueInfo& info = m_QueuedPlayers[plr->GetGUID()];
175 info.InviteTime = 0;
176 info.LastInviteTime = 0;
177 info.LastOnlineTime = getMSTime();
178 info.GroupInfo = ginfo;
180 // add the pinfo to ginfo's list
181 ginfo->Players[plr->GetGUID()] = &info;
184 //remove player from queue and from group info, if group info is empty then remove it too
185 void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCount)
187 //Player *plr = objmgr.GetPlayer(guid);
189 int32 queue_id = -1; // signed for proper for-loop finish
190 QueuedPlayersMap::iterator itr;
192 //remove player from map, if he's there
193 itr = m_QueuedPlayers.find(guid);
194 if( itr == m_QueuedPlayers.end() )
196 sLog.outError("BattleGroundQueue: couldn't find player to remove GUID: %u", GUID_LOPART(guid));
197 return;
200 GroupQueueInfo* group = itr->second.GroupInfo;
201 GroupsQueueType::iterator group_itr, group_itr_tmp;
202 // mostly people with the highest levels are in battlegrounds, thats why
203 // we count from MAX_BATTLEGROUND_QUEUES - 1 to 0
204 // variable index removes useless searching in other team's queue
205 uint32 index = (group->Team == HORDE) ? BG_TEAM_HORDE : BG_TEAM_ALLIANCE;
207 for (int32 queue_id_tmp = MAX_BATTLEGROUND_QUEUES - 1; queue_id_tmp >= 0 && queue_id == -1; --queue_id_tmp)
209 //we must check premade and normal team's queue - because when players from premade are joining bg,
210 //they leave groupinfo so we can't use its players size to find out index
211 for (uint32 j = index; j < BG_QUEUE_GROUP_TYPES_COUNT - 1; j += BG_QUEUE_NORMAL_ALLIANCE)
213 for(group_itr_tmp = m_QueuedGroups[queue_id_tmp][j].begin(); group_itr_tmp != m_QueuedGroups[queue_id_tmp][j].end(); ++group_itr_tmp)
215 if( (*group_itr_tmp) == group )
217 queue_id = queue_id_tmp;
218 group_itr = group_itr_tmp;
219 //we must store index to be able to erase iterator
220 index = j;
221 break;
226 //player can't be in queue without group, but just in case
227 if( queue_id == -1 )
229 sLog.outError("BattleGroundQueue: ERROR Cannot find groupinfo for player GUID: %u", GUID_LOPART(guid));
230 return;
232 sLog.outDebug("BattleGroundQueue: Removing player GUID %u, from queue_id %u", GUID_LOPART(guid), (uint32)queue_id);
234 // ALL variables are corrcetly set
235 // We can ignore leveling up in queue - it should not cause crash
236 // remove player from group
237 // if only one player there, remove group
239 // remove player queue info from group queue info
240 std::map<uint64, PlayerQueueInfo*>::iterator pitr = group->Players.find(guid);
241 if( pitr != group->Players.end() )
242 group->Players.erase(pitr);
244 // if invited to bg, and should decrease invited count, then do it
245 if( decreaseInvitedCount && group->IsInvitedToBGInstanceGUID )
247 BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID);
248 if( bg )
249 bg->DecreaseInvitedCount(group->Team);
252 // remove player queue info
253 m_QueuedPlayers.erase(itr);
255 //if we left BG queue(not porting) OR if arena team left queue for rated match
256 if( (decreaseInvitedCount && !group->ArenaType) || (group->ArenaType && group->IsRated && group->Players.empty()) )
257 AnnounceWorld(group, guid, false);
259 // remove group queue info if needed
260 if( group->Players.empty() )
262 m_QueuedGroups[queue_id][index].erase(group_itr);
263 delete group;
265 // if group wasn't empty, so it wasn't deleted, and player have left a rated
266 // queue -> everyone from the group should leave too
267 // don't remove recursively if already invited to bg!
268 else if( !group->IsInvitedToBGInstanceGUID && group->IsRated )
270 // remove next player, this is recursive
271 // first send removal information
272 if(Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first))
274 BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId);
275 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(group->BgTypeId, group->ArenaType);
276 uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId);
277 plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to
278 // queue->removeplayer, it causes bugs
279 WorldPacket data;
280 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr2->GetTeam(), queueSlot, STATUS_NONE, 0, 0);
281 plr2->GetSession()->SendPacket(&data);
283 // then actually delete, this may delete the group as well!
284 RemovePlayer(group->Players.begin()->first, decreaseInvitedCount);
288 //Announce world message
289 void BattleGroundQueue::AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playerGUID, bool isAddedToQueue)
291 if(ginfo->ArenaType) //if Arena
293 if( sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE) && ginfo->IsRated )
295 BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId);
296 if(!bg)
297 return;
299 char const* bgName = bg->GetName();
300 if(isAddedToQueue)
301 sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating);
302 else
303 sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating);
306 else //if BG
308 if( sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE) )
310 Player *plr = objmgr.GetPlayer(playerGUID);
311 BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId);
312 if(!bg || !plr)
313 return;
315 BGQueueIdBasedOnLevel queue_id = plr->GetBattleGroundQueueIdFromLevel(bg->GetTypeID());
316 char const* bgName = bg->GetName();
317 uint32 MinPlayers = bg->GetMinPlayersPerTeam();
318 uint32 qHorde = 0;
319 uint32 qAlliance = 0;
320 uint32 q_min_level = (queue_id + 1) * 10;
321 GroupsQueueType::const_iterator itr;
322 for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].end(); ++itr)
323 if( !(*itr)->IsInvitedToBGInstanceGUID )
324 qAlliance += (*itr)->Players.size();
325 for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].end(); ++itr)
326 if( !(*itr)->IsInvitedToBGInstanceGUID )
327 qHorde += (*itr)->Players.size();
329 // Show queue status to player only (when joining queue)
330 if( sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY) )
332 ChatHandler(plr).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF,
333 bgName, q_min_level, q_min_level + 10, qAlliance, MinPlayers, qHorde, MinPlayers);
335 // System message
336 else
338 sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD,
339 bgName, q_min_level, q_min_level + 10, qAlliance, MinPlayers, qHorde, MinPlayers);
345 bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side)
347 // set side if needed
348 if( side )
349 ginfo->Team = side;
351 if( !ginfo->IsInvitedToBGInstanceGUID )
353 // not yet invited
354 // set invitation
355 ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID();
356 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
357 // loop through the players
358 for(std::map<uint64,PlayerQueueInfo*>::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr)
360 // set status
361 itr->second->InviteTime = getMSTime();
362 itr->second->LastInviteTime = getMSTime();
364 // get the player
365 Player* plr = objmgr.GetPlayer(itr->first);
366 // if offline, skip him, this should not happen - player is removed from queue when he logs out
367 if( !plr )
368 continue;
370 // invite the player
371 sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID(), ginfo->Team);
373 WorldPacket data;
375 uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
377 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());
379 // send status packet
380 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, side?side:plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0);
381 plr->GetSession()->SendPacket(&data);
383 return true;
386 return false;
389 // used to remove the Enter Battle window if the battle has already ended, but someone still has it
390 // (this can happen in arenas mainly, since the preparation is shorter than the timer for the bgqueueremove event
391 void BattleGroundQueue::BGEndedRemoveInvites(BattleGround *bg)
393 BGQueueIdBasedOnLevel queue_id = bg->GetQueueId();
394 uint32 bgInstanceId = bg->GetInstanceID();
395 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
396 GroupsQueueType::iterator itr, next;
397 for(uint32 i = 0; i < BG_QUEUE_GROUP_TYPES_COUNT; i++)
399 for(itr = m_QueuedGroups[queue_id][i].begin(); itr != m_QueuedGroups[queue_id][i].end(); itr = next)
401 // must do this way, because the groupinfo will be deleted when all playerinfos are removed
402 GroupQueueInfo * ginfo = (*itr);
403 next = itr;
404 ++next;
405 // if group was invited to this bg instance, then remove all references
406 if( ginfo->IsInvitedToBGInstanceGUID == bgInstanceId )
408 // after removing this much playerinfos, the ginfo will be deleted, so we'll use a for loop
409 uint32 to_remove = ginfo->Players.size();
410 uint32 team = ginfo->Team;
411 for(uint32 j = 0; j < to_remove; j++)
413 // always remove the first one in the group
414 std::map<uint64, PlayerQueueInfo * >::iterator itr2 = ginfo->Players.begin();
415 if( itr2 == ginfo->Players.end() )
417 sLog.outError("Empty Players in ginfo, this should never happen!");
418 return;
420 // get the player
421 Player * plr = objmgr.GetPlayer(itr2->first);
422 if( !plr )
424 sLog.outError("Player offline when trying to remove from GroupQueueInfo, this should never happen.");
425 continue;
428 // get the queueslot
429 uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
430 if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue
432 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
433 // remove player from queue, this might delete the ginfo as well! don't use that pointer after this!
434 RemovePlayer(itr2->first, true);
435 WorldPacket data;
436 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, team, queueSlot, STATUS_NONE, 0, 0);
437 plr->GetSession()->SendPacket(&data);
446 This function is inviting players to already running battlegrounds
447 Invitation type is based on config file
448 large groups are disadvantageous, because they will be kicked first if invitation type = 1
450 void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel queue_id)
452 uint32 hordeFree = bg->GetFreeSlotsForTeam(HORDE);
453 uint32 aliFree = bg->GetFreeSlotsForTeam(ALLIANCE);
455 //iterator for iterating through bg queue
456 GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].begin();
457 //count of groups in queue - used to stop cycles
458 uint32 aliCount = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].size();
459 //index to queue which group is current
460 uint32 aliIndex = 0;
461 for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); aliIndex++)
462 ++Ali_itr;
463 //the same thing for horde
464 GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].begin();
465 uint32 hordeCount = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].size();
466 uint32 hordeIndex = 0;
467 for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); hordeIndex++)
468 ++Horde_itr;
470 //if ofc like BG queue invitation is set in config, then we are happy
471 if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0)
472 return;
475 if we reached this code, then we have to solve NP - complete problem called Subset sum problem
476 So one solution is to check all possible invitation subgroups, or we can use these conditions:
477 1. Last time when BattleGroundQueue::Update was executed we invited all possible players - so there is only small possibility
478 that we will invite now whole queue, because only 1 change has been made to queues from the last BattleGroundQueue::Update call
479 2. Other thing we should consider is group order in queue
482 // At first we need to compare free space in bg and our selection pool
483 int32 diffAli = aliFree - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount();
484 int32 diffHorde = hordeFree - m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount();
485 while( abs(diffAli - diffHorde) > 1 && (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() > 0) )
487 //each cycle execution we need to kick at least 1 group
488 if( diffAli < diffHorde )
490 //kick alliance group, add to pool new group if needed
491 if( m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffHorde - diffAli) )
493 for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), (aliFree >= diffHorde) ? aliFree - diffHorde : 0); aliIndex++)
494 ++Ali_itr;
496 //if ali selection is already empty, then kick horde group, but if there are less horde than ali in bg - break;
497 if( !m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() )
498 if( aliFree <= diffHorde - 1 )
499 break;
500 m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffHorde - diffAli);
502 else
504 //kick horde group, add to pool new group if needed
505 if( m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffAli - diffHorde) )
507 for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++)
508 ++Horde_itr;
510 if( !m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() )
511 if( hordeFree <= diffAli - 1 )
512 break;
513 m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde);
515 //count diffs after small update
516 diffAli = aliFree - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount();
517 diffHorde = hordeFree - m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount();
521 // this method checks if premade versus premade battleground is possible
522 // then after 30 mins (default) in queue it moves premade group to normal queue
523 // it tries to invite as much players as it can - to MaxPlayersPerTeam, because premade groups have more than MinPlayersPerTeam players
524 bool BattleGroundQueue::CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32 MaxPlayersPerTeam, uint32 MinPlayersPerTeam)
526 //check match
527 if(!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && !m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].empty())
529 //start premade match
530 //if groups aren't invited
531 GroupsQueueType::const_iterator ali_group, horde_group;
532 for( ali_group = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); ali_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++ali_group)
533 if( !(*ali_group)->IsInvitedToBGInstanceGUID )
534 break;
535 for( horde_group = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].begin(); horde_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end(); ++horde_group)
536 if( !(*horde_group)->IsInvitedToBGInstanceGUID )
537 break;
539 if( ali_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end())
541 m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam);
542 m_SelectionPools[BG_TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam);
543 //add groups/players from normal queue to size of bigger group
544 uint32 maxPlayers = std::max(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount());
545 GroupsQueueType::const_iterator itr;
546 for(uint32 i = 0; i < BG_TEAMS_COUNT; i++)
548 for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr)
550 if( !(*itr)->IsInvitedToBGInstanceGUID && m_SelectionPools[i].AddGroup((*itr), maxPlayers) )
551 break;
554 //premade selection pools are set
555 return true;
558 // now check if we can move group from Premade queue to normal queue (timer has expired) or group size lowered!!
559 // this could be 2 cycles but i'm checking only first team in queue - it can cause problem -
560 // 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
561 // and when they click or after 80 seconds the queue info is removed from queue
562 uint32 time_before = getMSTime() - sWorld.getConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH);
563 for(uint32 i = 0; i < BG_TEAMS_COUNT; i++)
565 if(!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].empty())
567 GroupsQueueType::iterator itr = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].begin();
568 if(!(*itr)->IsInvitedToBGInstanceGUID && ((*itr)->JoinTime < time_before || (*itr)->Players.size() < MinPlayersPerTeam))
570 //we must insert group to normal queue and erase pointer from premade queue
571 m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr));
572 m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr); //pop_front();
576 //selection pools are not set
577 return false;
580 //this function tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam
581 bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BGQueueIdBasedOnLevel queue_id, uint32 MinPlayersPerTeam)
583 uint32 minPlayers = bg_template->GetMinPlayersPerTeam();
584 uint32 maxPlayers = bg_template->GetMaxPlayersPerTeam();
586 GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT];
587 for(uint32 i = 0; i < BG_TEAMS_COUNT; i++)
589 itr_team[i] = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin();
590 for(; itr_team[i] != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++(itr_team[i]))
592 if( !(*(itr_team[i]))->IsInvitedToBGInstanceGUID )
594 m_SelectionPools[i].AddGroup(*(itr_team[i]), maxPlayers);
595 if( m_SelectionPools[i].GetPlayerCount() >= minPlayers )
596 break;
600 //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
601 uint32 j = BG_TEAM_ALLIANCE;
602 if( m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() )
603 j = BG_TEAM_HORDE;
604 if( sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != 0
605 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers )
607 //we will try to invite more groups to team with less players indexed by j
608 ++(itr_team[j]); //this will not cause a crash, because for cycle above reached break;
609 for(; itr_team[j] != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + j].end(); ++(itr_team[j]))
611 if( !(*(itr_team[j]))->IsInvitedToBGInstanceGUID )
612 if( m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % BG_TEAMS_COUNT].GetPlayerCount()) )
613 break;
615 // do not allow to start bg with more than 2 players more on 1 faction
616 if( abs((int32)(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())) > 2 )
617 return false;
620 //return true if there are enough players in selection pools - enable to work .debug bg command correctly
621 return m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= MinPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= MinPlayersPerTeam;
625 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
626 it must be called after fully adding the members of a group to ensure group joining
627 should be called from BattleGround::RemovePlayer function in some cases
629 void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType, bool isRated, uint32 arenaRating)
631 // this can happen when removing last player from battleground
632 if( queue_id == MAX_BATTLEGROUND_QUEUES )
633 return;
635 //if no players in queue ... do nothing
636 if( m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].empty() &&
637 m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].empty() &&
638 m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].empty() &&
639 m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].empty() )
640 return;
642 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, arenaType);
644 //battleground with free slot for player should be always in the beggining of the queue
645 // maybe it would be better to create bgfreeslotqueue for each queue_id_based_on_level
646 BGFreeSlotQueueType::iterator itr, next;
647 for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next)
649 next = itr;
650 ++next;
651 // DO NOT allow queue manager to invite new player to arena
652 if( (*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetQueueId() == queue_id &&
653 (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE )
655 BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!)
656 // and iterator is invalid
658 // clear selection pools
659 m_SelectionPools[BG_TEAM_ALLIANCE].Init();
660 m_SelectionPools[BG_TEAM_HORDE].Init();
662 // call a function that does the job for us
663 FillPlayersToBG(bg, queue_id);
665 // now everything is set, invite players
666 for(GroupsQueueType::const_iterator itr = m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.begin(); itr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++itr)
667 InviteGroupToBG((*itr), bg, (*itr)->Team);
668 for(GroupsQueueType::const_iterator itr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); itr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++itr)
669 InviteGroupToBG((*itr), bg, (*itr)->Team);
671 if( !bg->HasFreeSlots() )
673 // remove BG from BGFreeSlotQueue
674 bg->RemoveFromBGFreeSlotQueue();
679 // finished iterating through the bgs with free slots, maybe we need to create a new bg
681 BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
682 if( !bg_template )
684 sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId);
685 return;
687 // get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!)
688 uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam();
689 uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam();
690 if( sBattleGroundMgr.isTesting() )
691 MinPlayersPerTeam = 1;
692 if( bg_template->isArena() )
694 if( sBattleGroundMgr.isArenaTesting() )
696 MaxPlayersPerTeam = 1;
697 MinPlayersPerTeam = 1;
699 else
701 //this switch can be much shorter
702 MaxPlayersPerTeam = arenaType;
703 MinPlayersPerTeam = arenaType;
704 /*switch(arenaType)
706 case ARENA_TYPE_2v2:
707 MaxPlayersPerTeam = 2;
708 MinPlayersPerTeam = 2;
709 break;
710 case ARENA_TYPE_3v3:
711 MaxPlayersPerTeam = 3;
712 MinPlayersPerTeam = 3;
713 break;
714 case ARENA_TYPE_5v5:
715 MaxPlayersPerTeam = 5;
716 MinPlayersPerTeam = 5;
717 break;
722 m_SelectionPools[BG_TEAM_ALLIANCE].Init();
723 m_SelectionPools[BG_TEAM_HORDE].Init();
725 if( bg_template->isBattleGround() )
727 //check if there is premade against premade match
728 if( CheckPremadeMatch(queue_id, bg_template->GetMaxPlayersPerTeam(), bg_template->GetMinPlayersPerTeam()) )
730 //create new battleground
731 BattleGround * bg2 = NULL;
732 if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, queue_id, 0, false)) )
734 sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId);
735 return;
737 //invite those selection pools
738 for(uint32 i = 0; i < BG_TEAMS_COUNT; i++)
739 for(GroupsQueueType::const_iterator itr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); itr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++itr)
740 InviteGroupToBG((*itr), bg2, (*itr)->Team);
741 //start bg
742 bg2->StartBattleGround();
743 //clear structures
744 m_SelectionPools[BG_TEAM_ALLIANCE].Init();
745 m_SelectionPools[BG_TEAM_HORDE].Init();
749 // now check if there are in queues enough players to start new game of (normal battleground, or non-rated arena)
750 if( !isRated )
752 // if there are enough players in pools, start new battleground or non rated arena
753 if( CheckNormalMatch(bg_template, queue_id, MinPlayersPerTeam) )
755 // we successfully created a pool
756 BattleGround * bg2 = NULL;
757 if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, queue_id, arenaType, false)) )
759 sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId);
760 return;
763 // invite those selection pools
764 for(uint32 i = 0; i < BG_TEAMS_COUNT; i++)
765 for(GroupsQueueType::const_iterator itr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); itr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++itr)
766 InviteGroupToBG((*itr), bg2, (*itr)->Team);
767 // start bg
768 bg2->StartBattleGround();
771 else if( bg_template->isArena() )
773 // found out the minimum and maximum ratings the newly added team should battle against
774 // arenaRating is the rating of the latest joined team, or 0
775 // 0 is on (automatic update call) and we must set it to team's with longest wait time
776 if ( !arenaRating )
778 GroupQueueInfo* front1 = NULL;
779 GroupQueueInfo* front2 = NULL;
780 if( !m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].empty() )
782 front1 = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].front();
783 arenaRating = front1->ArenaTeamRating;
785 if( !m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].empty() )
787 front2 = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].front();
788 arenaRating = front2->ArenaTeamRating;
790 if( front1 && front2 )
792 if( front1->JoinTime < front2->JoinTime )
793 arenaRating = front1->ArenaTeamRating;
795 else if( !front1 && !front2 )
796 return; //queues are empty
799 //set rating range
800 uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference();
801 uint32 arenaMaxRating = arenaRating + sBattleGroundMgr.GetMaxRatingDifference();
802 // if max rating difference is set and the time past since server startup is greater than the rating discard time
803 // (after what time the ratings aren't taken into account when making teams) then
804 // the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account
805 // else leave the discard time on 0, this way all ratings will be discarded
806 uint32 discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer();
808 // we need to find 2 teams which will play next game
810 GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT];
812 //optimalization : --- we dont need to use selection_pools - each update we select max 2 groups
814 for(uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++)
816 // take the group that joined first
817 itr_team[i] = m_QueuedGroups[queue_id][i].begin();
818 for(; itr_team[i] != m_QueuedGroups[queue_id][i].end(); ++(itr_team[i]))
820 // if group match conditions, then add it to pool
821 if( !(*itr_team[i])->IsInvitedToBGInstanceGUID
822 && (((*itr_team[i])->ArenaTeamRating >= arenaMinRating && (*itr_team[i])->ArenaTeamRating <= arenaMaxRating)
823 || (*itr_team[i])->JoinTime < discardTime) )
825 m_SelectionPools[i].AddGroup((*itr_team[i]), MaxPlayersPerTeam);
826 // break for cycle to be able to start selecting another group from same faction queue
827 break;
831 // now we are done if we have 2 groups - ali vs horde!
832 // if we don't have, we must try to continue search in same queue
833 // tmp variables are correctly set
834 // this code isn't much userfriendly - but it is supposed to continue search for mathing group in HORDE queue
835 if( m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() )
837 itr_team[BG_TEAM_ALLIANCE] = itr_team[BG_TEAM_HORDE];
838 ++itr_team[BG_TEAM_ALLIANCE];
839 for(; itr_team[BG_TEAM_ALLIANCE] != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[BG_TEAM_ALLIANCE]))
841 if( !(*itr_team[BG_TEAM_ALLIANCE])->IsInvitedToBGInstanceGUID
842 && (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating)
843 || (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime) )
845 m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam);
846 break;
850 // this code isn't much userfriendly - but it is supposed to continue search for mathing group in ALLIANCE queue
851 if( m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() )
853 itr_team[BG_TEAM_HORDE] = itr_team[BG_TEAM_ALLIANCE];
854 ++itr_team[BG_TEAM_HORDE];
855 for(; itr_team[BG_TEAM_HORDE] != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[BG_TEAM_HORDE]))
857 if( !(*itr_team[BG_TEAM_HORDE])->IsInvitedToBGInstanceGUID
858 && (((*itr_team[BG_TEAM_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_HORDE])->ArenaTeamRating <= arenaMaxRating)
859 || (*itr_team[BG_TEAM_HORDE])->JoinTime < discardTime) )
861 m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam);
862 break;
867 //if we have 2 teams, then start new arena and invite players!
868 if( m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() )
870 BattleGround* arena = NULL;
871 if( !(arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, queue_id, arenaType, true)) )
873 sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!");
874 return;
877 (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating;
878 sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating);
879 (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating;
880 sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamId, (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating);
881 // 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
882 if( (*(itr_team[BG_TEAM_ALLIANCE]))->Team != ALLIANCE )
884 // add to alliance queue
885 m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[BG_TEAM_ALLIANCE]));
886 // erase from horde queue
887 m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[BG_TEAM_ALLIANCE]);
889 if( (*(itr_team[BG_TEAM_HORDE]))->Team != HORDE )
891 m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[BG_TEAM_HORDE]));
892 m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]);
895 InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE);
896 InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE);
898 sLog.outDebug("Starting rated arena match!");
900 arena->StartBattleGround();
905 /*********************************************************/
906 /*** BATTLEGROUND QUEUE EVENTS ***/
907 /*********************************************************/
909 bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
911 Player* plr = objmgr.GetPlayer( m_PlayerGuid );
913 // player logged off (we should do nothing, he is correctly removed from queue in another procedure)
914 if (!plr)
915 return true;
917 // Player can be in another BG queue and must be removed in normal way in any case
919 BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID);
920 if (!bg)
921 return true;
923 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
924 uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
925 if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue
927 // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems
928 BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers;
929 BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid);
930 if (qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID)
932 WorldPacket data;
933 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, qItr->second.GroupInfo->Team, queueSlot, STATUS_WAIT_JOIN, INVITATION_REMIND_TIME, 0);
934 plr->GetSession()->SendPacket(&data);
937 return true; //event will be deleted
940 void BGQueueInviteEvent::Abort(uint64 /*e_time*/)
942 //this should not be called
943 sLog.outError("Battleground invite event ABORTED!");
946 bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
948 Player* plr = objmgr.GetPlayer( m_PlayerGuid );
949 if (!plr)
950 // player logged off (we should do nothing, he is correctly removed from queue in another procedure)
951 return true;
953 BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID);
954 if (!bg)
955 return true;
957 sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID);
959 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
960 uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
961 if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue
963 // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems
964 BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers;
965 BattleGroundQueue::QueuedPlayersMap::iterator qMapItr = qpMap.find(m_PlayerGuid);
966 if (qMapItr != qpMap.end() && qMapItr->second.GroupInfo && qMapItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID)
968 if (qMapItr->second.GroupInfo->IsRated)
970 ArenaTeam * at = objmgr.GetArenaTeamById(qMapItr->second.GroupInfo->ArenaTeamId);
971 if (at)
973 sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(plr->GetGUID()), qMapItr->second.GroupInfo->OpponentsTeamRating);
974 at->MemberLost(plr, qMapItr->second.GroupInfo->OpponentsTeamRating);
975 at->SaveToDB();
978 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
979 sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(m_PlayerGuid, true);
980 sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bg->GetTypeID(), bg->GetQueueId());
981 WorldPacket data;
982 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, m_PlayersTeam, queueSlot, STATUS_NONE, 0, 0);
983 plr->GetSession()->SendPacket(&data);
986 else
987 sLog.outDebug("Battleground: Player was already removed from queue");
989 //event will be deleted
990 return true;
993 void BGQueueRemoveEvent::Abort(uint64 /*e_time*/)
995 //this should not be called
996 sLog.outError("Battleground remove event ABORTED!");
999 /*********************************************************/
1000 /*** BATTLEGROUND MANAGER ***/
1001 /*********************************************************/
1003 BattleGroundMgr::BattleGroundMgr() : m_AutoDistributionTimeChecker(0), m_ArenaTesting(false)
1005 m_BattleGrounds.clear();
1006 m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER);
1007 m_Testing=false;
1010 BattleGroundMgr::~BattleGroundMgr()
1012 DeleteAlllBattleGrounds();
1015 void BattleGroundMgr::DeleteAlllBattleGrounds()
1017 for(BattleGroundSet::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end();)
1019 BattleGround * bg = itr->second;
1020 m_BattleGrounds.erase(itr++);
1021 delete bg;
1024 // destroy template battlegrounds that listed only in queues (other already terminated)
1025 for(uint32 bgTypeId = 0; bgTypeId < MAX_BATTLEGROUND_TYPE_ID; ++bgTypeId)
1027 // ~BattleGround call unregistring BG from queue
1028 while(!BGFreeSlotQueue[bgTypeId].empty())
1029 delete BGFreeSlotQueue[bgTypeId].front();
1033 // used to update running battlegrounds, and delete finished ones
1034 void BattleGroundMgr::Update(uint32 diff)
1036 BattleGroundSet::iterator itr, next;
1037 for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next)
1039 next = itr;
1040 ++next;
1041 itr->second->Update(diff);
1042 // use the SetDeleteThis variable
1043 // direct deletion caused crashes
1044 if(itr->second->m_SetDeleteThis)
1046 BattleGround * bg = itr->second;
1047 m_BattleGrounds.erase(itr);
1048 delete bg;
1051 // if rating difference counts, maybe force-update queues
1052 if(sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE) && sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER))
1054 // it's time to force update
1055 if(m_NextRatingDiscardUpdate < diff)
1057 // forced update for level 70 rated arenas
1058 sLog.outDebug("BattleGroundMgr: UPDATING ARENA QUEUES");
1059 m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_79, ARENA_TYPE_2v2, true, 0);
1060 m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_80, ARENA_TYPE_2v2, true, 0);
1061 m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_79, ARENA_TYPE_3v3, true, 0);
1062 m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_80, ARENA_TYPE_3v3, true, 0);
1063 m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_79, ARENA_TYPE_5v5, true, 0);
1064 m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_80, ARENA_TYPE_5v5, true, 0);
1065 m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER);
1067 else
1068 m_NextRatingDiscardUpdate -= diff;
1070 if(sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS))
1072 if(m_AutoDistributionTimeChecker < diff)
1074 if(sWorld.GetGameTime() > m_NextAutoDistributionTime)
1076 DistributeArenaPoints();
1077 m_NextAutoDistributionTime = sWorld.GetGameTime() + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS);
1078 CharacterDatabase.PExecute("UPDATE saved_variables SET NextArenaPointDistributionTime = '"I64FMTD"'", m_NextAutoDistributionTime);
1080 m_AutoDistributionTimeChecker = 600000; // check 10 minutes
1082 else
1083 m_AutoDistributionTimeChecker -= diff;
1087 void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype, uint8 israted)
1089 // we can be in 3 queues in same time...
1090 if(StatusID == 0)
1092 data->Initialize(SMSG_BATTLEFIELD_STATUS, 4*3);
1093 *data << uint32(QueueSlot); // queue id (0...2)
1094 *data << uint64(0);
1095 return;
1098 data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4));
1099 *data << uint32(QueueSlot); // queue id (0...2) - player can be in 3 queues in time
1100 // uint64 in client
1101 *data << uint64( uint64(arenatype ? arenatype : bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) );
1102 *data << uint32(0); // unknown
1103 // alliance/horde for BG and skirmish/rated for Arenas
1104 *data << uint8(bg->isArena() ? ( israted ? israted : bg->isRated() ) : bg->GetTeamIndexByTeamId(team));
1105 /* *data << uint8(arenatype ? arenatype : bg->GetArenaType()); // team type (0=BG, 2=2x2, 3=3x3, 5=5x5), for arenas // NOT PROPER VALUE IF ARENA ISN'T RUNNING YET!!!!
1106 switch(bg->GetTypeID()) // value depends on bg id
1108 case BATTLEGROUND_AV:
1109 *data << uint8(1);
1110 break;
1111 case BATTLEGROUND_WS:
1112 *data << uint8(2);
1113 break;
1114 case BATTLEGROUND_AB:
1115 *data << uint8(3);
1116 break;
1117 case BATTLEGROUND_NA:
1118 *data << uint8(4);
1119 break;
1120 case BATTLEGROUND_BE:
1121 *data << uint8(5);
1122 break;
1123 case BATTLEGROUND_AA:
1124 *data << uint8(6);
1125 break;
1126 case BATTLEGROUND_EY:
1127 *data << uint8(7);
1128 break;
1129 case BATTLEGROUND_RL:
1130 *data << uint8(8);
1131 break;
1132 case BATTLEGROUND_SA:
1133 *data << uint8(9);
1134 break;
1135 case BATTLEGROUND_DS:
1136 *data << uint8(10);
1137 break;
1138 case BATTLEGROUND_RV:
1139 *data << uint8(11);
1140 break;
1141 default: // unknown
1142 *data << uint8(0);
1143 break;
1146 if(bg->isArena() && (StatusID == STATUS_WAIT_QUEUE))
1147 *data << uint32(BATTLEGROUND_AA); // all arenas I don't think so.
1148 else
1149 *data << uint32(bg->GetTypeID()); // BG id from DBC
1151 *data << uint16(0x1F90); // unk value 8080
1152 *data << uint32(bg->GetInstanceID()); // instance id
1154 if(bg->isBattleGround())
1155 *data << uint8(bg->GetTeamIndexByTeamId(team)); // team
1156 else
1157 *data << uint8(israted?israted:bg->isRated()); // is rated battle
1159 *data << uint32(StatusID); // status
1160 switch(StatusID)
1162 case STATUS_WAIT_QUEUE: // status_in_queue
1163 *data << uint32(Time1); // average wait time, milliseconds
1164 *data << uint32(Time2); // time in queue, updated every minute?
1165 break;
1166 case STATUS_WAIT_JOIN: // status_invite
1167 *data << uint32(bg->GetMapId()); // map id
1168 *data << uint32(Time1); // time to remove from queue, milliseconds
1169 break;
1170 case STATUS_IN_PROGRESS: // status_in_progress
1171 *data << uint32(bg->GetMapId()); // map id
1172 *data << uint32(Time1); // 0 at bg start, 120000 after bg end, time to bg auto leave, milliseconds
1173 *data << uint32(Time2); // time from bg start, milliseconds
1174 *data << uint8(0x1); // unk sometimes 0x0!
1175 break;
1176 default:
1177 sLog.outError("Unknown BG status!");
1178 break;
1182 void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg)
1184 uint8 type = (bg->isArena() ? 1 : 0);
1185 // last check on 3.0.3
1186 data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize()));
1187 *data << uint8(type); // type (battleground=0/arena=1)
1189 if(type) // arena
1191 // it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H
1192 for(int i = 1; i >= 0; --i)
1194 *data << uint32(bg->m_ArenaTeamRatingChanges[i]);
1195 *data << uint32(3999); // huge thanks for TOM_RUS for this!
1196 sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]);
1198 for(int i = 1; i >= 0; --i)
1200 uint32 at_id = bg->m_ArenaTeamIds[i];
1201 ArenaTeam * at = objmgr.GetArenaTeamById(at_id);
1202 if(at)
1203 *data << at->GetName();
1204 else
1205 *data << (uint8)0;
1209 if(bg->GetStatus() != STATUS_WAIT_LEAVE)
1211 *data << uint8(0); // bg not ended
1213 else
1215 *data << uint8(1); // bg ended
1216 *data << uint8(bg->GetWinner()); // who win
1219 *data << (int32)(bg->GetPlayerScoresSize());
1221 for(std::map<uint64, BattleGroundScore*>::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr)
1223 *data << (uint64)itr->first;
1224 *data << (int32)itr->second->KillingBlows;
1225 if(type == 0)
1227 *data << (int32)itr->second->HonorableKills;
1228 *data << (int32)itr->second->Deaths;
1229 *data << (int32)(itr->second->BonusHonor);
1231 else
1233 Player *plr = objmgr.GetPlayer(itr->first);
1234 uint32 team = bg->GetPlayerTeam(itr->first);
1235 if(!team && plr)
1236 team = plr->GetTeam();
1237 if( ( bg->GetWinner()==0 && team == ALLIANCE ) || ( bg->GetWinner()==1 && team==HORDE ) )
1238 *data << uint8(1);
1239 else
1240 *data << uint8(0);
1242 *data << (int32)itr->second->DamageDone; // damage done
1243 *data << (int32)itr->second->HealingDone; // healing done
1244 switch(bg->GetTypeID()) // battleground specific things
1246 case BATTLEGROUND_AV:
1247 *data << (uint32)0x00000005; // count of next fields
1248 *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted; // GraveyardsAssaulted
1249 *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsDefended; // GraveyardsDefended
1250 *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersAssaulted; // TowersAssaulted
1251 *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersDefended; // TowersDefended
1252 *data << (uint32)((BattleGroundAVScore*)itr->second)->MinesCaptured; // MinesCaptured
1253 break;
1254 case BATTLEGROUND_WS:
1255 *data << (uint32)0x00000002; // count of next fields
1256 *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagCaptures; // flag captures
1257 *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagReturns; // flag returns
1258 break;
1259 case BATTLEGROUND_AB:
1260 *data << (uint32)0x00000002; // count of next fields
1261 *data << (uint32)((BattleGroundABScore*)itr->second)->BasesAssaulted; // bases asssulted
1262 *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended
1263 break;
1264 case BATTLEGROUND_EY:
1265 *data << (uint32)0x00000001; // count of next fields
1266 *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures
1267 break;
1268 case BATTLEGROUND_NA:
1269 case BATTLEGROUND_BE:
1270 case BATTLEGROUND_AA:
1271 case BATTLEGROUND_RL:
1272 case BATTLEGROUND_SA: // wotlk
1273 case BATTLEGROUND_DS: // wotlk
1274 case BATTLEGROUND_RV: // wotlk
1275 *data << (int32)0; // 0
1276 break;
1277 default:
1278 sLog.outDebug("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID());
1279 *data << (int32)0;
1280 break;
1285 void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, BattleGroundTypeId bgTypeId)
1287 /*bgTypeId is:
1288 0 - Your group has joined a battleground queue, but you are not eligible
1289 1 - Your group has joined the queue for AV
1290 2 - Your group has joined the queue for WS
1291 3 - Your group has joined the queue for AB
1292 4 - Your group has joined the queue for NA
1293 5 - Your group has joined the queue for BE Arena
1294 6 - Your group has joined the queue for All Arenas
1295 7 - Your group has joined the queue for EotS*/
1296 data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4);
1297 *data << uint32(bgTypeId);
1300 void BattleGroundMgr::BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value)
1302 data->Initialize(SMSG_UPDATE_WORLD_STATE, 4+4);
1303 *data << uint32(field);
1304 *data << uint32(value);
1307 void BattleGroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid)
1309 data->Initialize(SMSG_PLAY_SOUND, 4);
1310 *data << uint32(soundid);
1313 void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, Player *plr)
1315 data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8);
1316 *data << uint64(plr->GetGUID());
1319 void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr)
1321 data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8);
1322 *data << uint64(plr->GetGUID());
1325 void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team)
1327 // set invited player counters:
1328 BattleGround* bg = GetBattleGround(bgInstanceGUID);
1329 if(!bg)
1330 return;
1331 bg->IncreaseInvitedCount(team);
1333 plr->SetInviteForBattleGroundQueueType(BGQueueTypeId(bg->GetTypeID(),bg->GetArenaType()), bgInstanceGUID);
1335 // set the arena teams for rated matches
1336 if(bg->isArena() && bg->isRated())
1338 switch(bg->GetArenaType())
1340 case ARENA_TYPE_2v2:
1341 bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(0));
1342 break;
1343 case ARENA_TYPE_3v3:
1344 bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(1));
1345 break;
1346 case ARENA_TYPE_5v5:
1347 bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(2));
1348 break;
1349 default:
1350 break;
1354 // create invite events:
1355 //add events to player's counters ---- this is not good way - there should be something like global event processor, where we should add those events
1356 BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), bgInstanceGUID);
1357 plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITATION_REMIND_TIME));
1358 BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, team);
1359 plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME));
1362 BattleGround * BattleGroundMgr::GetBattleGroundTemplate(BattleGroundTypeId bgTypeId)
1364 return BGFreeSlotQueue[bgTypeId].empty() ? NULL : BGFreeSlotQueue[bgTypeId].back();
1367 // create a new battleground that will really be used to play
1368 BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType, bool isRated)
1370 // get the template BG
1371 BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId);
1372 if(!bg_template)
1374 sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId);
1375 return NULL;
1378 //for arenas there is random map used
1379 if(bg_template->isArena())
1381 BattleGroundTypeId arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL};
1382 uint32 arena_num = urand(0,2);
1383 bgTypeId = arenas[arena_num];
1384 bg_template = GetBattleGroundTemplate(bgTypeId);
1385 if(!bg_template)
1387 sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId);
1388 return NULL;
1392 BattleGround *bg = NULL;
1393 // create a copy of the BG template
1394 switch(bgTypeId)
1396 case BATTLEGROUND_AV:
1397 bg = new BattleGroundAV(*(BattleGroundAV*)bg_template);
1398 break;
1399 case BATTLEGROUND_WS:
1400 bg = new BattleGroundWS(*(BattleGroundWS*)bg_template);
1401 break;
1402 case BATTLEGROUND_AB:
1403 bg = new BattleGroundAB(*(BattleGroundAB*)bg_template);
1404 break;
1405 case BATTLEGROUND_NA:
1406 bg = new BattleGroundNA(*(BattleGroundNA*)bg_template);
1407 break;
1408 case BATTLEGROUND_BE:
1409 bg = new BattleGroundBE(*(BattleGroundBE*)bg_template);
1410 break;
1411 case BATTLEGROUND_AA:
1412 bg = new BattleGroundAA(*(BattleGroundAA*)bg_template);
1413 break;
1414 case BATTLEGROUND_EY:
1415 bg = new BattleGroundEY(*(BattleGroundEY*)bg_template);
1416 break;
1417 case BATTLEGROUND_RL:
1418 bg = new BattleGroundRL(*(BattleGroundRL*)bg_template);
1419 break;
1420 case BATTLEGROUND_SA:
1421 bg = new BattleGroundSA(*(BattleGroundSA*)bg_template);
1422 break;
1423 case BATTLEGROUND_DS:
1424 bg = new BattleGroundDS(*(BattleGroundDS*)bg_template);
1425 break;
1426 case BATTLEGROUND_RV:
1427 bg = new BattleGroundRV(*(BattleGroundRV*)bg_template);
1428 break;
1429 default:
1430 //error, but it is handled few lines above
1431 return 0;
1434 // generate a new instance id
1435 bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id
1437 // reset the new bg (set status to status_wait_queue from status_none)
1438 bg->Reset();
1440 if( bg->isBattleGround() && sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE) )
1442 sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, bg->GetName(), bg->GetMinLevel(), bg->GetMaxLevel());
1445 // start the joining of the bg
1446 bg->SetStatus(STATUS_WAIT_JOIN);
1447 bg->SetQueueId(queue_id);
1448 // initialize arena / rating info
1449 bg->SetArenaType(arenaType);
1450 // set rating
1451 bg->SetRated(isRated);
1453 /* will be setup in BG::Update() when the first player is ported in
1454 if(!(bg->SetupBattleGround()))
1457 // add BG to free slot queue
1458 bg->AddToBGFreeSlotQueue();
1460 // add bg to update list
1461 AddBattleGround(bg->GetInstanceID(), bg);
1463 return bg;
1466 // used to create the BG templates
1467 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)
1469 // Create the BG
1470 BattleGround *bg = NULL;
1471 switch(bgTypeId)
1473 case BATTLEGROUND_AV: bg = new BattleGroundAV; break;
1474 case BATTLEGROUND_WS: bg = new BattleGroundWS; break;
1475 case BATTLEGROUND_AB: bg = new BattleGroundAB; break;
1476 case BATTLEGROUND_NA: bg = new BattleGroundNA; break;
1477 case BATTLEGROUND_BE: bg = new BattleGroundBE; break;
1478 case BATTLEGROUND_AA: bg = new BattleGroundAA; break;
1479 case BATTLEGROUND_EY: bg = new BattleGroundEY; break;
1480 case BATTLEGROUND_RL: bg = new BattleGroundRL; break;
1481 case BATTLEGROUND_SA: bg = new BattleGroundSA; break;
1482 case BATTLEGROUND_DS: bg = new BattleGroundDS; break;
1483 case BATTLEGROUND_RV: bg = new BattleGroundRV; break;
1484 default:bg = new BattleGround; break; // placeholder for non implemented BG
1487 bg->SetMapId(MapID);
1489 bg->Reset();
1491 bg->SetTypeID(bgTypeId);
1492 bg->SetInstanceID(0);
1493 bg->SetArenaorBGType(IsArena);
1494 bg->SetMinPlayersPerTeam(MinPlayersPerTeam);
1495 bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam);
1496 bg->SetMinPlayers(MinPlayersPerTeam * 2);
1497 bg->SetMaxPlayers(MaxPlayersPerTeam * 2);
1498 bg->SetName(BattleGroundName);
1499 bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO);
1500 bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO);
1501 bg->SetLevelRange(LevelMin, LevelMax);
1503 //add BattleGround instance to FreeSlotQueue (.back() will return the template!)
1504 bg->AddToBGFreeSlotQueue();
1506 // do NOT add to update list, since this is a template battleground!
1508 // return some not-null value, bgTypeId is good enough for me
1509 return bgTypeId;
1512 void BattleGroundMgr::CreateInitialBattleGrounds()
1514 float AStartLoc[4];
1515 float HStartLoc[4];
1516 uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2;
1517 BattlemasterListEntry const *bl;
1518 WorldSafeLocsEntry const *start;
1519 bool IsArena;
1521 uint32 count = 0;
1523 // 0 1 2 3 4 5 6 7 8
1524 QueryResult *result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO FROM battleground_template");
1526 if(!result)
1528 barGoLink bar(1);
1530 bar.step();
1532 sLog.outString();
1533 sLog.outErrorDb(">> Loaded 0 battlegrounds. DB table `battleground_template` is empty.");
1534 return;
1537 barGoLink bar(result->GetRowCount());
1541 Field *fields = result->Fetch();
1542 bar.step();
1544 uint32 bgTypeID_ = fields[0].GetUInt32();
1546 // can be overwrite by values from DB
1547 bl = sBattlemasterListStore.LookupEntry(bgTypeID_);
1548 if(!bl)
1550 sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeID_);
1551 continue;
1554 BattleGroundTypeId bgTypeID = BattleGroundTypeId(bgTypeID_);
1556 IsArena = (bl->type == TYPE_ARENA);
1557 MinPlayersPerTeam = fields[1].GetUInt32();
1558 MaxPlayersPerTeam = fields[2].GetUInt32();
1559 MinLvl = fields[3].GetUInt32();
1560 MaxLvl = fields[4].GetUInt32();
1561 //check values from DB
1562 if( MaxPlayersPerTeam == 0 || MinPlayersPerTeam == 0 || MinPlayersPerTeam > MaxPlayersPerTeam )
1564 MaxPlayersPerTeam = bl->maxplayersperteam;
1565 MinPlayersPerTeam = bl->maxplayersperteam / 2;
1567 if( MinLvl == 0 || MaxLvl == 0 || MinLvl > MaxLvl )
1569 MinLvl = bl->minlvl;
1570 MaxLvl = bl->maxlvl;
1573 start1 = fields[5].GetUInt32();
1575 start = sWorldSafeLocsStore.LookupEntry(start1);
1576 if(start)
1578 AStartLoc[0] = start->x;
1579 AStartLoc[1] = start->y;
1580 AStartLoc[2] = start->z;
1581 AStartLoc[3] = fields[6].GetFloat();
1583 else if(bgTypeID == BATTLEGROUND_AA)
1585 AStartLoc[0] = 0;
1586 AStartLoc[1] = 0;
1587 AStartLoc[2] = 0;
1588 AStartLoc[3] = fields[6].GetFloat();
1590 else
1592 sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTypeID, start1);
1593 continue;
1596 start2 = fields[7].GetUInt32();
1598 start = sWorldSafeLocsStore.LookupEntry(start2);
1599 if(start)
1601 HStartLoc[0] = start->x;
1602 HStartLoc[1] = start->y;
1603 HStartLoc[2] = start->z;
1604 HStartLoc[3] = fields[8].GetFloat();
1606 else if(bgTypeID == BATTLEGROUND_AA)
1608 HStartLoc[0] = 0;
1609 HStartLoc[1] = 0;
1610 HStartLoc[2] = 0;
1611 HStartLoc[3] = fields[8].GetFloat();
1613 else
1615 sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTypeID, start2);
1616 continue;
1619 //sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl);
1620 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]))
1621 continue;
1623 ++count;
1624 } while (result->NextRow());
1626 delete result;
1628 sLog.outString();
1629 sLog.outString( ">> Loaded %u battlegrounds", count );
1632 void BattleGroundMgr::InitAutomaticArenaPointDistribution()
1634 if(sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS))
1636 sLog.outDebug("Initializing Automatic Arena Point Distribution");
1637 QueryResult * result = CharacterDatabase.Query("SELECT NextArenaPointDistributionTime FROM saved_variables");
1638 if(!result)
1640 sLog.outDebug("Battleground: Next arena point distribution time not found in SavedVariables, reseting it now.");
1641 m_NextAutoDistributionTime = sWorld.GetGameTime() + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS);
1642 CharacterDatabase.PExecute("INSERT INTO saved_variables (NextArenaPointDistributionTime) VALUES ('"I64FMTD"')", m_NextAutoDistributionTime);
1644 else
1646 m_NextAutoDistributionTime = (*result)[0].GetUInt64();
1647 delete result;
1649 sLog.outDebug("Automatic Arena Point Distribution initialized.");
1653 void BattleGroundMgr::DistributeArenaPoints()
1655 // used to distribute arena points based on last week's stats
1656 sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_START);
1658 sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_START);
1660 //temporary structure for storing maximum points to add values for all players
1661 std::map<uint32, uint32> PlayerPoints;
1663 //at first update all points for all team members
1664 for(ObjectMgr::ArenaTeamMap::iterator team_itr = objmgr.GetArenaTeamMapBegin(); team_itr != objmgr.GetArenaTeamMapEnd(); ++team_itr)
1666 if(ArenaTeam * at = team_itr->second)
1668 at->UpdateArenaPointsHelper(PlayerPoints);
1672 //cycle that gives points to all players
1673 for (std::map<uint32, uint32>::iterator plr_itr = PlayerPoints.begin(); plr_itr != PlayerPoints.end(); ++plr_itr)
1675 //update to database
1676 CharacterDatabase.PExecute("UPDATE characters SET arena_pending_points = '%u' WHERE `guid` = '%u'", plr_itr->second, plr_itr->first);
1677 //add points if player is online
1678 Player* pl = objmgr.GetPlayer(plr_itr->first);
1679 if (pl)
1680 pl->ModifyArenaPoints(plr_itr->second);
1683 PlayerPoints.clear();
1685 sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_END);
1687 sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_START);
1688 for(ObjectMgr::ArenaTeamMap::iterator titr = objmgr.GetArenaTeamMapBegin(); titr != objmgr.GetArenaTeamMapEnd(); ++titr)
1690 if(ArenaTeam * at = titr->second)
1692 at->FinishWeek(); // set played this week etc values to 0 in memory, too
1693 at->SaveToDB(); // save changes
1694 at->NotifyStatsChanged(); // notify the players of the changes
1698 sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_END);
1700 sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_END);
1703 void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player* plr, BattleGroundTypeId bgTypeId)
1705 uint32 PlayerLevel = 10;
1707 if(plr)
1708 PlayerLevel = plr->getLevel();
1710 data->Initialize(SMSG_BATTLEFIELD_LIST);
1711 *data << uint64(guid); // battlemaster guid
1712 *data << uint32(bgTypeId); // battleground id
1713 if(bgTypeId == BATTLEGROUND_AA) // arena
1715 *data << uint8(5); // unk
1716 *data << uint32(0); // unk
1718 else // battleground
1720 *data << uint8(0x00); // unk
1722 size_t count_pos = data->wpos();
1723 uint32 count = 0;
1724 *data << uint32(0x00); // number of bg instances
1726 for(std::map<uint32, BattleGround*>::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr)
1728 if(itr->second->GetTypeID() == bgTypeId && (PlayerLevel >= itr->second->GetMinLevel()) && (PlayerLevel <= itr->second->GetMaxLevel()))
1730 *data << uint32(itr->second->GetInstanceID());
1731 ++count;
1734 data->put<uint32>( count_pos , count);
1738 void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId)
1740 BattleGround *bg = GetBattleGround(instanceId);
1741 if(bg)
1743 uint32 mapid = bg->GetMapId();
1744 float x, y, z, O;
1745 uint32 team = pl->GetBGTeam();
1746 if(team==0)
1747 team = pl->GetTeam();
1748 bg->GetTeamStartLoc(team, x, y, z, O);
1750 sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O);
1751 pl->TeleportTo(mapid, x, y, z, O);
1753 else
1755 sLog.outError("player %u trying to port to non-existent bg instance %u",pl->GetGUIDLow(), instanceId);
1759 void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid)
1761 WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12);
1762 uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds
1763 if(time_ == uint32(-1))
1764 time_ = 0;
1765 data << guid << time_;
1766 pl->GetSession()->SendPacket(&data);
1769 bool BattleGroundMgr::IsArenaType(BattleGroundTypeId bgTypeId)
1771 return ( bgTypeId == BATTLEGROUND_AA ||
1772 bgTypeId == BATTLEGROUND_BE ||
1773 bgTypeId == BATTLEGROUND_NA ||
1774 bgTypeId == BATTLEGROUND_RL );
1777 BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType)
1779 switch(bgTypeId)
1781 case BATTLEGROUND_WS:
1782 return BATTLEGROUND_QUEUE_WS;
1783 case BATTLEGROUND_AB:
1784 return BATTLEGROUND_QUEUE_AB;
1785 case BATTLEGROUND_AV:
1786 return BATTLEGROUND_QUEUE_AV;
1787 case BATTLEGROUND_EY:
1788 return BATTLEGROUND_QUEUE_EY;
1789 case BATTLEGROUND_SA:
1790 return BATTLEGROUND_QUEUE_SA;
1791 case BATTLEGROUND_AA:
1792 case BATTLEGROUND_NA:
1793 case BATTLEGROUND_RL:
1794 case BATTLEGROUND_BE:
1795 case BATTLEGROUND_DS:
1796 case BATTLEGROUND_RV:
1797 switch(arenaType)
1799 case ARENA_TYPE_2v2:
1800 return BATTLEGROUND_QUEUE_2v2;
1801 case ARENA_TYPE_3v3:
1802 return BATTLEGROUND_QUEUE_3v3;
1803 case ARENA_TYPE_5v5:
1804 return BATTLEGROUND_QUEUE_5v5;
1805 default:
1806 return BATTLEGROUND_QUEUE_NONE;
1808 default:
1809 return BATTLEGROUND_QUEUE_NONE;
1813 BattleGroundTypeId BattleGroundMgr::BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId)
1815 switch(bgQueueTypeId)
1817 case BATTLEGROUND_QUEUE_WS:
1818 return BATTLEGROUND_WS;
1819 case BATTLEGROUND_QUEUE_AB:
1820 return BATTLEGROUND_AB;
1821 case BATTLEGROUND_QUEUE_AV:
1822 return BATTLEGROUND_AV;
1823 case BATTLEGROUND_QUEUE_EY:
1824 return BATTLEGROUND_EY;
1825 case BATTLEGROUND_QUEUE_SA:
1826 return BATTLEGROUND_SA;
1827 case BATTLEGROUND_QUEUE_2v2:
1828 case BATTLEGROUND_QUEUE_3v3:
1829 case BATTLEGROUND_QUEUE_5v5:
1830 return BATTLEGROUND_AA;
1831 default:
1832 return BattleGroundTypeId(0); // used for unknown template (it existed and do nothing)
1836 uint8 BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId bgQueueTypeId)
1838 switch(bgQueueTypeId)
1840 case BATTLEGROUND_QUEUE_2v2:
1841 return ARENA_TYPE_2v2;
1842 case BATTLEGROUND_QUEUE_3v3:
1843 return ARENA_TYPE_3v3;
1844 case BATTLEGROUND_QUEUE_5v5:
1845 return ARENA_TYPE_5v5;
1846 default:
1847 return 0;
1851 void BattleGroundMgr::ToggleTesting()
1853 m_Testing = !m_Testing;
1854 if(m_Testing)
1855 sWorld.SendWorldText(LANG_DEBUG_BG_ON);
1856 else
1857 sWorld.SendWorldText(LANG_DEBUG_BG_OFF);
1860 void BattleGroundMgr::ToggleArenaTesting()
1862 m_ArenaTesting = !m_ArenaTesting;
1863 if(m_ArenaTesting)
1864 sWorld.SendWorldText(LANG_DEBUG_ARENA_ON);
1865 else
1866 sWorld.SendWorldText(LANG_DEBUG_ARENA_OFF);
1869 uint32 BattleGroundMgr::GetMaxRatingDifference() const
1871 // this is for stupid people who can't use brain and set max rating difference to 0
1872 uint32 diff = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE);
1873 if (diff == 0)
1874 diff = 5000;
1875 return diff;
1878 uint32 BattleGroundMgr::GetRatingDiscardTimer() const
1880 return sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER);
1883 uint32 BattleGroundMgr::GetPrematureFinishTime() const
1885 return sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER);
1888 void BattleGroundMgr::LoadBattleMastersEntry()
1890 mBattleMastersMap.clear(); // need for reload case
1892 QueryResult *result = WorldDatabase.Query( "SELECT entry,bg_template FROM battlemaster_entry" );
1894 uint32 count = 0;
1896 if( !result )
1898 barGoLink bar( 1 );
1899 bar.step();
1901 sLog.outString();
1902 sLog.outString( ">> Loaded 0 battlemaster entries - table is empty!" );
1903 return;
1906 barGoLink bar( result->GetRowCount() );
1910 ++count;
1911 bar.step();
1913 Field *fields = result->Fetch();
1915 uint32 entry = fields[0].GetUInt32();
1916 uint32 bgTypeId = fields[1].GetUInt32();
1917 if (!sBattlemasterListStore.LookupEntry(bgTypeId))
1919 sLog.outErrorDb("Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.",entry,bgTypeId);
1920 continue;
1923 mBattleMastersMap[entry] = BattleGroundTypeId(bgTypeId);
1925 } while( result->NextRow() );
1927 delete result;
1929 sLog.outString();
1930 sLog.outString( ">> Loaded %u battlemaster entries", count );