root/trunk/src/game/BattleGroundMgr.cpp @ 44

Revision 44, 72.6 kB (checked in by yumileroy, 17 years ago)

[svn] * Merge Temp dev SVN with Assembla.
* Changes include:

  • Implementation of w12x's Outdoor PvP and Game Event Systems.
  • Temporary removal of IRC Chat Bot (until infinite loop when disabled is fixed).
  • All mangos -> trinity (to convert your mangos_string table, please run mangos_string_to_trinity_string.sql).
  • Improved Config cleanup.
  • And many more changes.

Original author: Seline
Date: 2008-10-14 11:57:03-05:00

Line 
1/*
2 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
3 *
4 * Thanks to the original authors: MaNGOS <http://www.mangosproject.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "Common.h"
22#include "Player.h"
23#include "BattleGroundMgr.h"
24#include "BattleGroundAV.h"
25#include "BattleGroundAB.h"
26#include "BattleGroundEY.h"
27#include "BattleGroundWS.h"
28#include "BattleGroundNA.h"
29#include "BattleGroundBE.h"
30#include "BattleGroundAA.h"
31#include "BattleGroundRL.h"
32#include "SharedDefines.h"
33#include "Policies/SingletonImp.h"
34#include "MapManager.h"
35#include "Map.h"
36#include "MapInstanced.h"
37#include "ObjectMgr.h"
38#include "ProgressBar.h"
39#include "World.h"
40#include "ArenaTeam.h"
41#include "Chat.h"
42
43INSTANTIATE_SINGLETON_1( BattleGroundMgr );
44
45/*********************************************************/
46/***            BATTLEGROUND QUEUE SYSTEM              ***/
47/*********************************************************/
48
49BattleGroundQueue::BattleGroundQueue()
50{
51    //queues are empty, we don't have to call clear()
52/*    for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++)
53    {
54        //m_QueuedPlayers[i].Horde = 0;
55        //m_QueuedPlayers[i].Alliance = 0;
56        //m_QueuedPlayers[i].AverageTime = 0;
57    }*/
58}
59
60BattleGroundQueue::~BattleGroundQueue()
61{
62    for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++)
63    {
64        m_QueuedPlayers[i].clear();
65        for(QueuedGroupsList::iterator itr = m_QueuedGroups[i].begin(); itr!= m_QueuedGroups[i].end(); ++itr)
66        {
67            delete (*itr);
68        }
69        m_QueuedGroups[i].clear();
70    }
71}
72
73// initialize eligible groups from the given source matching the given specifications
74void BattleGroundQueue::EligibleGroups::Init(BattleGroundQueue::QueuedGroupsList *source, uint32 BgTypeId, uint32 side, uint32 MaxPlayers, uint8 ArenaType, bool IsRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime, uint32 excludeTeam)
75{
76    // clear from prev initialization
77    clear();
78    BattleGroundQueue::QueuedGroupsList::iterator itr, next;
79    // iterate through the source
80    for(itr = source->begin(); itr!= source->end(); itr = next)
81    {
82        next = itr;
83        ++next;
84        if( (*itr)->BgTypeId == BgTypeId &&     // bg type must match
85            (*itr)->ArenaType == ArenaType &&   // arena type must match
86            (*itr)->IsRated == IsRated &&       // israted must match
87            (*itr)->IsInvitedToBGInstanceGUID == 0 && // leave out already invited groups
88            (*itr)->Team == side &&             // match side
89            (*itr)->Players.size() <= MaxPlayers &&   // the group must fit in the bg
90            ( !excludeTeam || (*itr)->ArenaTeamId != excludeTeam ) && // if excludeTeam is specified, leave out those arena team ids
91            ( !IsRated || (*itr)->Players.size() == MaxPlayers ) &&   // if rated, then pass only if the player count is exact NEEDS TESTING! (but now this should never happen)
92            (  (*itr)->JoinTime <= DisregardTime              // pass if disregard time is greater than join time
93               || (*itr)->ArenaTeamRating == 0                 // pass if no rating info
94               || ( (*itr)->ArenaTeamRating >= MinRating       // pass if matches the rating range
95                     && (*itr)->ArenaTeamRating <= MaxRating ) ) )   
96        {
97            // the group matches the conditions
98            // insert it in order of groupsize, and join time
99            uint32 size = (*itr)->Players.size();
100            uint32 jointime = (*itr)->JoinTime;
101            bool inserted = false;
102
103            for(std::list<GroupQueueInfo *>::iterator elig_itr = begin(); elig_itr != end(); ++elig_itr)
104            {
105                // if the next one's size is smaller, then insert
106                // also insert if the next one's size is equal, but it joined the queue later
107                if( ((*elig_itr)->Players.size()<size) ||
108                    ((*elig_itr)->Players.size() == size && (*elig_itr)->JoinTime > jointime) )
109                {
110                    insert(elig_itr,(*itr));
111                    inserted = true;
112                    break;
113                }
114            }
115            // if not inserted -> this is the smallest group -> push_back
116            if(!inserted)
117            {
118                push_back((*itr));
119            }
120        }
121    }
122}
123
124// remove group from eligible groups
125// used when building selection pools
126void BattleGroundQueue::EligibleGroups::RemoveGroup(GroupQueueInfo * ginfo)
127{
128    for(std::list<GroupQueueInfo *>::iterator itr = begin(); itr != end(); ++itr)
129    {
130        if((*itr)==ginfo)
131        {
132            erase(itr);
133            return;
134        }
135    }
136}
137
138// selection pool initialization, used to clean up from prev selection
139void BattleGroundQueue::SelectionPool::Init()
140{
141    SelectedGroups.clear();
142    MaxGroup = 0;
143    PlayerCount = 0;
144}
145
146// get the maximal group from the selection pool
147// used when building the pool, and have to remove the largest
148GroupQueueInfo * BattleGroundQueue::SelectionPool::GetMaximalGroup()
149{
150    if(SelectedGroups.empty())
151    {
152        sLog.outError("Getting max group when selection pool is empty, this should never happen.");
153        MaxGroup = NULL;
154        return 0;
155    }
156    // actually select the max group if it's not set
157    if(MaxGroup==0 && !SelectedGroups.empty())
158    {
159        uint32 max_size = 0;
160        for(std::list<GroupQueueInfo *>::iterator itr = SelectedGroups.begin(); itr != SelectedGroups.end(); ++itr)
161        {
162            if(max_size<(*itr)->Players.size())
163            {
164                MaxGroup =(*itr);
165                max_size = MaxGroup->Players.size();
166            }
167        }
168    }
169    return MaxGroup;
170}
171
172// remove group info from selection pool
173// used when building selection pools and have to remove maximal group
174void BattleGroundQueue::SelectionPool::RemoveGroup(GroupQueueInfo *ginfo)
175{
176    // uninitiate max group info if needed
177    if(MaxGroup == ginfo)
178        MaxGroup = 0;
179    // find what to remove
180    for(std::list<GroupQueueInfo *>::iterator itr = SelectedGroups.begin(); itr != SelectedGroups.end(); ++itr)
181    {
182        if((*itr)==ginfo)
183        {
184            SelectedGroups.erase(itr);
185            // decrease selected players count
186            PlayerCount -= ginfo->Players.size();
187            return;
188        }
189    }
190}
191
192// add group to selection
193// used when building selection pools
194void BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo * ginfo)
195{
196    SelectedGroups.push_back(ginfo);
197    // increase selected players count
198    PlayerCount+=ginfo->Players.size();
199    if(!MaxGroup || ginfo->Players.size() > MaxGroup->Players.size())
200    {
201        // update max group info if needed
202        MaxGroup = ginfo;
203    }
204}
205
206// add group to bg queue with the given leader and bg specifications
207GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, uint32 BgTypeId, uint8 ArenaType, bool isRated, uint32 arenaRating, uint32 arenateamid)
208{
209    uint32 queue_id = leader->GetBattleGroundQueueIdFromLevel();
210
211    // create new ginfo
212    // cannot use the method like in addplayer, because that could modify an in-queue group's stats
213    // (e.g. leader leaving queue then joining as individual again)
214    GroupQueueInfo* ginfo = new GroupQueueInfo;
215    ginfo->BgTypeId                  = BgTypeId;
216    ginfo->ArenaType                 = ArenaType;
217    ginfo->ArenaTeamId               = arenateamid;
218    ginfo->IsRated                   = isRated;
219    ginfo->IsInvitedToBGInstanceGUID = 0;                       // maybe this should be modifiable by function arguments to enable selection of running instances?
220    ginfo->JoinTime                  = getMSTime();
221    ginfo->Team                      = leader->GetTeam();
222
223    if(sBattleGroundMgr.GetMaxRatingDifference())               // if max difference is set, then store rating info for queue
224        ginfo->ArenaTeamRating       = arenaRating;
225    else
226        ginfo->ArenaTeamRating       = 0;                       // don't if it doesn't matter
227
228    ginfo->Players.clear();
229
230    m_QueuedGroups[queue_id].push_back(ginfo);
231
232    // return ginfo, because it is needed to add players to this group info
233    return ginfo;
234}
235
236void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo)
237{
238    uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel();
239
240    //if player isn't in queue, he is added, if already is, then values are overwritten, no memory leak
241    PlayerQueueInfo& info = m_QueuedPlayers[queue_id][plr->GetGUID()];
242    info.InviteTime                 = 0;
243    info.LastInviteTime             = 0;
244    info.LastOnlineTime             = getMSTime();
245    info.GroupInfo                  = ginfo;
246
247    // add the pinfo to ginfo's list
248    ginfo->Players[plr->GetGUID()]  = &info;
249}
250
251void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount)
252{
253    Player *plr = objmgr.GetPlayer(guid);
254
255    uint32 queue_id = 0;
256    QueuedPlayersMap::iterator itr;
257    GroupQueueInfo * group;
258    QueuedGroupsList::iterator group_itr;
259    bool IsSet = false;
260    if(plr)
261    {
262        queue_id = plr->GetBattleGroundQueueIdFromLevel();
263
264        itr = m_QueuedPlayers[queue_id].find(guid);
265        if(itr != m_QueuedPlayers[queue_id].end())
266            IsSet = true;
267    }
268
269    if(!IsSet)
270    {                                                       
271        // either player is offline, or he levelled up to another queue category
272        // sLog.outError("Battleground: removing offline player from BG queue - this might not happen, but it should not cause crash");
273        for (uint32 i = 0; i < MAX_BATTLEGROUND_QUEUES; i++)
274        {
275            itr = m_QueuedPlayers[i].find(guid);
276            if(itr != m_QueuedPlayers[i].end())
277            {
278                queue_id = i;
279                IsSet = true;
280                break;
281            }
282        }
283    }
284
285    // couldn't find the player in bg queue, return
286    if(!IsSet)
287    {
288        sLog.outError("Battleground: couldn't find player to remove.");
289        return;
290    }
291
292    group = itr->second.GroupInfo;
293
294    for(group_itr=m_QueuedGroups[queue_id].begin(); group_itr != m_QueuedGroups[queue_id].end(); ++group_itr)
295    {
296        if(group == (GroupQueueInfo*)(*group_itr))
297            break;
298    }
299
300    // variables are set (what about leveling up when in queue????)
301    // remove player from group
302    // if only player there, remove group
303
304    // remove player queue info from group queue info
305    std::map<uint64, PlayerQueueInfo*>::iterator pitr = group->Players.find(guid);
306
307    if(pitr != group->Players.end())
308        group->Players.erase(pitr);
309
310    // check for iterator correctness
311    if (group_itr != m_QueuedGroups[queue_id].end() && itr != m_QueuedPlayers[queue_id].end())
312    {
313        // used when player left the queue, NOT used when porting to bg
314        if (decreaseInvitedCount)
315        {
316            // if invited to bg, and should decrease invited count, then do it
317            if(group->IsInvitedToBGInstanceGUID)
318            {
319                BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID);
320                if (bg)
321                    bg->DecreaseInvitedCount(group->Team);
322                if (bg && !bg->GetPlayersSize() && !bg->GetInvitedCount(ALLIANCE) && !bg->GetInvitedCount(HORDE))
323                {
324                    // no more players on battleground, set delete it
325                    bg->SetDeleteThis();
326                }
327            }
328            // update the join queue, maybe now the player's group fits in a queue!
329            // not yet implemented (should store bgTypeId in group queue info?)
330        }
331        // remove player queue info
332        m_QueuedPlayers[queue_id].erase(itr);
333        // remove group queue info if needed
334        if(group->Players.empty())
335        {
336            m_QueuedGroups[queue_id].erase(group_itr);
337            delete group;
338        }
339        // NEEDS TESTING!
340        // group wasn't empty, so it wasn't deleted, and player have left a rated queue -> everyone from the group should leave too
341        // don't remove recursively if already invited to bg!
342        else if(!group->IsInvitedToBGInstanceGUID && decreaseInvitedCount && group->IsRated)
343        {
344            // remove next player, this is recursive
345            // first send removal information
346            if(Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first))
347            {
348                BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId);
349                uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(group->BgTypeId,group->ArenaType);
350                uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId);
351                plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs
352                WorldPacket data;
353                sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr2->GetTeam(), queueSlot, STATUS_NONE, 0, 0);
354                plr2->GetSession()->SendPacket(&data);
355            }
356            // then actually delete, this may delete the group as well!
357            RemovePlayer(group->Players.begin()->first,decreaseInvitedCount);
358        }
359    }
360}
361
362bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side)
363{
364    // set side if needed
365    if(side)
366        ginfo->Team = side;
367
368    if(!ginfo->IsInvitedToBGInstanceGUID)
369    {
370        // not yet invited
371        // set invitation
372        ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID();
373        uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
374        // loop through the players
375        for(std::map<uint64,PlayerQueueInfo*>::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr)
376        {
377            // set status
378            itr->second->InviteTime = getMSTime();
379            itr->second->LastInviteTime = getMSTime();
380
381            // get the player
382            Player* plr = objmgr.GetPlayer(itr->first);
383            // if offline, skip him
384            if(!plr)
385                continue;
386
387            // invite the player
388            sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID(),ginfo->Team);
389
390            WorldPacket data;
391
392            uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
393
394            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());
395
396            // send status packet
397            sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, side?side:plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0);
398            plr->GetSession()->SendPacket(&data);
399        }
400        return true;
401    }
402
403    return false;
404}
405
406// this function is responsible for the selection of queued groups when trying to create new battlegrounds
407bool BattleGroundQueue::BuildSelectionPool(uint32 bgTypeId, uint32 queue_id, uint32 MinPlayers, uint32 MaxPlayers,  SelectionPoolBuildMode mode, uint8 ArenaType, bool isRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime, uint32 excludeTeam)
408{
409    uint32 side;
410    switch(mode)
411    {
412    case NORMAL_ALLIANCE:
413    case ONESIDE_ALLIANCE_TEAM1:
414    case ONESIDE_ALLIANCE_TEAM2:
415        side = ALLIANCE;
416        break;
417    case NORMAL_HORDE:
418    case ONESIDE_HORDE_TEAM1:
419    case ONESIDE_HORDE_TEAM2:
420        side = HORDE;
421        break;
422    default:
423        //unknown mode, return false
424        sLog.outDebug("Battleground: unknown selection pool build mode, returning...");
425        return false;
426        break;
427    }
428
429    // inititate the groups eligible to create the bg
430    m_EligibleGroups.Init(&(m_QueuedGroups[queue_id]), bgTypeId, side, MaxPlayers, ArenaType, isRated, MinRating, MaxRating, DisregardTime, excludeTeam);
431    // init the selected groups (clear)
432    m_SelectionPools[mode].Init();
433    while(!(m_EligibleGroups.empty()))
434    {
435        sLog.outDebug("m_EligibleGroups is not empty, continue building selection pool");
436        // in decreasing group size, add groups to join if they fit in the MaxPlayersPerTeam players
437        for(EligibleGroups::iterator itr= m_EligibleGroups.begin(); itr!=m_EligibleGroups.end(); ++itr)
438        {
439            // get the maximal not yet checked group
440            GroupQueueInfo * MaxGroup = (*itr);
441            // if it fits in the maxplayer size, add it
442            if( (m_SelectionPools[mode].GetPlayerCount() + MaxGroup->Players.size()) <= MaxPlayers )
443            {
444                m_SelectionPools[mode].AddGroup(MaxGroup);
445            }
446        }
447        if(m_SelectionPools[mode].GetPlayerCount()>=MinPlayers)
448        {
449            // the selection pool is set, return
450            sLog.outDebug("pool build succeeded, return true");
451            return true;
452        }
453        // if the selection pool's not set, then remove the group with the highest player count, and try again with the rest.
454        GroupQueueInfo * MaxGroup = m_SelectionPools[mode].GetMaximalGroup();
455        m_EligibleGroups.RemoveGroup(MaxGroup);
456        m_SelectionPools[mode].RemoveGroup(MaxGroup);
457    }
458    // failed to build a selection pool matching the given values
459    return false;
460}
461
462// used to remove the Enter Battle window if the battle has already, but someone still has it
463// (this can happen in arenas mainly, since the preparation is shorter than the timer for the bgqueueremove event
464void BattleGroundQueue::BGEndedRemoveInvites(BattleGround *bg)
465{
466    uint32 queue_id = bg->GetQueueType();
467    uint32 bgInstanceId = bg->GetInstanceID();
468    uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
469    QueuedGroupsList::iterator itr, next;
470    for(itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); itr = next)
471    {
472        // must do this way, because the groupinfo will be deleted when all playerinfos are removed
473        GroupQueueInfo * ginfo = (*itr);
474        next = itr;
475        ++next;
476        // if group was invited to this bg instance, then remove all references
477        if(ginfo->IsInvitedToBGInstanceGUID == bgInstanceId)
478        {
479            // after removing this much playerinfos, the ginfo will be deleted, so we'll use a for loop
480            uint32 to_remove = ginfo->Players.size();
481            uint32 team = ginfo->Team;
482            for(int i = 0; i < to_remove; ++i)
483            {
484                // always remove the first one in the group
485                std::map<uint64, PlayerQueueInfo * >::iterator itr2 = ginfo->Players.begin();
486                if(itr2 == ginfo->Players.end())
487                {
488                    sLog.outError("Empty Players in ginfo, this should never happen!");
489                    return;
490                }
491
492                // get the player
493                Player * plr = objmgr.GetPlayer(itr2->first);
494                if(!plr)
495                {
496                    sLog.outError("Player offline when trying to remove from GroupQueueInfo, this should never happen.");
497                    continue;
498                }
499
500                // get the queueslot
501                uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
502                if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue
503                {
504                    plr->RemoveBattleGroundQueueId(bgQueueTypeId);
505                    // remove player from queue, this might delete the ginfo as well! don't use that pointer after this!
506                    RemovePlayer(itr2->first, true);
507                    // this is probably unneeded, since this player was already invited -> does not fit when initing eligible groups
508                    // but updateing the queue can't hurt
509                    Update(bgQueueTypeId, bg->GetQueueType());
510                    // send info to client
511                    WorldPacket data;
512                    sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, team, queueSlot, STATUS_NONE, 0, 0);
513                    plr->GetSession()->SendPacket(&data);
514                }
515            }
516        }
517    }
518}
519
520/*
521this 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
522it must be called after fully adding the members of a group to ensure group joining
523should be called after removeplayer functions in some cases
524*/
525void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype, bool isRated, uint32 arenaRating)
526{
527    if (queue_id >= MAX_BATTLEGROUND_QUEUES)
528    {
529        //this is error, that caused crashes (not in , but now it shouldn't)
530        sLog.outError("BattleGroundQueue::Update() called for non existing queue type - this can cause crash, pls report problem, if this is the last line of error log before crash");
531        return;
532    }
533
534    //if no players in queue ... do nothing
535    if (this->m_QueuedGroups[queue_id].size() == 0)
536        return;
537
538    uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype);
539
540    //battleground with free slot for player should be always the last in this queue
541    BGFreeSlotQueueType::iterator itr, next;
542    for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next)
543    {
544        next = itr;
545        ++next;
546        // battleground is running, so if:
547        // DO NOT allow queue manager to invite new player to running arena
548        if ((*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE)
549        {
550            //we must check both teams
551            BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!)
552            // and iterator is invalid
553
554            for(QueuedGroupsList::iterator itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); ++itr)
555            {
556                // did the group join for this bg type?
557                if((*itr)->BgTypeId != bgTypeId)
558                    continue;
559                // if so, check if fits in
560                if(bg->GetFreeSlotsForTeam((*itr)->Team) >= (*itr)->Players.size())
561                {
562                    // if group fits in, invite it
563                    InviteGroupToBG((*itr),bg,(*itr)->Team);
564                }
565            }
566
567            if (!bg->HasFreeSlots())
568            {
569                //remove BG from BGFreeSlotQueue
570                bg->RemoveFromBGFreeSlotQueue();
571            }
572        }
573    }
574
575    // finished iterating through the bgs with free slots, maybe we need to create a new bg
576
577    BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
578    if(!bg_template)
579    {
580        sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId);
581        return;
582    }
583
584    // get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!)
585    uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam();
586    uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam();
587    if(bg_template->isArena())
588    {
589        if(sBattleGroundMgr.isArenaTesting())
590        {
591            MaxPlayersPerTeam = 1;
592            MinPlayersPerTeam = 1;
593        }
594        else
595        {
596            switch(arenatype)
597            {
598            case ARENA_TYPE_2v2:
599                MaxPlayersPerTeam = 2;
600                MinPlayersPerTeam = 2;
601                break;
602            case ARENA_TYPE_3v3:
603                MaxPlayersPerTeam = 3;
604                MinPlayersPerTeam = 3;
605                break;
606            case ARENA_TYPE_5v5:
607                MaxPlayersPerTeam = 5;
608                MinPlayersPerTeam = 5;
609                break;
610            }
611        }
612    }
613
614    // found out the minimum and maximum ratings the newly added team should battle against
615    // arenaRating is the rating of the latest joined team
616    uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference();
617    // if no rating is specified, set maxrating to 0
618    uint32 arenaMaxRating = (arenaRating == 0)? 0 : arenaRating + sBattleGroundMgr.GetMaxRatingDifference();
619    uint32 discardTime = 0;
620    // if max rating difference is set and the time past since server startup is greater than the rating discard time
621    // (after what time the ratings aren't taken into account when making teams) then
622    // the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account
623    // else leave the discard time on 0, this way all ratings will be discarded
624    if(sBattleGroundMgr.GetMaxRatingDifference() && getMSTime() >= sBattleGroundMgr.GetRatingDiscardTimer())
625        discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer();
626
627    // try to build the selection pools
628    bool bAllyOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_ALLIANCE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime);
629    if(bAllyOK)
630        sLog.outDebug("Battleground: ally pool succesfully build");
631    else
632        sLog.outDebug("Battleground: ally pool wasn't created");
633    bool bHordeOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_HORDE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime);
634    if(bHordeOK)
635        sLog.outDebug("Battleground: horde pool succesfully built");
636    else
637        sLog.outDebug("Battleground: horde pool wasn't created");
638
639    // if selection pools are ready, create the new bg
640    if (bAllyOK && bHordeOK)
641    {
642        BattleGround * bg2 = 0;
643        // special handling for arenas
644        if(bg_template->isArena())
645        {
646            // Find a random arena, that can be created
647            uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL};
648            uint32 arena_num = urand(0,2);
649            if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) &&
650                !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) &&
651                !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) )
652            {
653                sLog.outError("Battleground: couldn't create arena");
654                return;
655            }
656
657            // set the MaxPlayersPerTeam values based on arenatype
658            // setting the min player values isn't needed, since we won't be using that value later on.
659            if(sBattleGroundMgr.isArenaTesting())
660            {
661                bg2->SetMaxPlayersPerTeam(1);
662                bg2->SetMaxPlayers(2);
663            }
664            else
665            {
666                switch(arenatype)
667                {
668                case ARENA_TYPE_2v2:
669                    bg2->SetMaxPlayersPerTeam(2);
670                    bg2->SetMaxPlayers(4);
671                    break;
672                case ARENA_TYPE_3v3:
673                    bg2->SetMaxPlayersPerTeam(3);
674                    bg2->SetMaxPlayers(6);
675                    break;
676                case ARENA_TYPE_5v5:
677                    bg2->SetMaxPlayersPerTeam(5);
678                    bg2->SetMaxPlayers(10);
679                    break;
680                default:
681                    break;
682                }
683            }
684        }
685        else
686        {
687            // create new battleground
688            bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId);
689        }
690
691        if(!bg2)
692        {
693            sLog.outError("Battleground: couldn't create bg %u",bgTypeId);
694            return;
695        }
696
697        // start the joining of the bg
698        bg2->SetStatus(STATUS_WAIT_JOIN);
699        bg2->SetQueueType(queue_id);
700        // initialize arena / rating info
701        bg2->SetArenaType(arenatype);
702        // set rating
703        bg2->SetRated(isRated);
704
705        std::list<GroupQueueInfo* >::iterator itr;
706
707        // invite groups from horde selection pool
708        for(itr = m_SelectionPools[NORMAL_HORDE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_HORDE].SelectedGroups.end(); ++itr)
709        {
710            InviteGroupToBG((*itr),bg2,HORDE);
711        }
712
713        // invite groups from ally selection pools
714        for(itr = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.end(); ++itr)
715        {
716            InviteGroupToBG((*itr),bg2,ALLIANCE);
717        }
718
719        // start the battleground
720        bg2->StartBattleGround();
721    }
722
723    // there weren't enough players for a "normal" match
724    // if arena, enable horde versus horde or alliance versus alliance teams here
725
726    else if(bg_template->isArena())
727    {
728        bool bOneSideHordeTeam1 = false, bOneSideHordeTeam2 = false;
729        bool bOneSideAllyTeam1 = false, bOneSideAllyTeam2 = false;
730        bOneSideHordeTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime);
731        if(bOneSideHordeTeam1)
732        {
733            // one team has been selected, find out if other can be selected too
734            std::list<GroupQueueInfo* >::iterator itr;
735            // temporarily change the team side to enable building the next pool excluding the already selected groups
736            for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr)
737                (*itr)->Team=ALLIANCE;
738
739            bOneSideHordeTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime, (*(m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin()))->ArenaTeamId);
740
741            // change back the team to horde
742            for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr)
743                (*itr)->Team=HORDE;
744
745            if(!bOneSideHordeTeam2)
746                bOneSideHordeTeam1 = false;
747        }
748        if(!bOneSideHordeTeam1)
749        {
750            // check for one sided ally
751            bOneSideAllyTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime);
752            if(bOneSideAllyTeam1)
753            {
754                // one team has been selected, find out if other can be selected too
755                std::list<GroupQueueInfo* >::iterator itr;
756                // temporarily change the team side to enable building the next pool excluding the already selected groups
757                for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr)
758                    (*itr)->Team=HORDE;
759
760                bOneSideAllyTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime,(*(m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin()))->ArenaTeamId);
761
762                // change back the team to ally
763                for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr)
764                    (*itr)->Team=ALLIANCE;
765            }
766
767            if(!bOneSideAllyTeam2)
768                bOneSideAllyTeam1 = false;
769        }
770        // 1-sided BuildSelectionPool() will work, because the MinPlayersPerTeam == MaxPlayersPerTeam in every arena!!!!
771        if( (bOneSideHordeTeam1 && bOneSideHordeTeam2) ||
772            (bOneSideAllyTeam1 && bOneSideAllyTeam2) )
773        {
774            // which side has enough players?
775            uint32 side = 0;
776            SelectionPoolBuildMode mode1, mode2;
777            // find out what pools are we using
778            if(bOneSideAllyTeam1 && bOneSideAllyTeam2)
779            {
780                side = ALLIANCE;
781                mode1 = ONESIDE_ALLIANCE_TEAM1;
782                mode2 = ONESIDE_ALLIANCE_TEAM2;
783            }
784            else
785            {
786                side = HORDE;
787                mode1 = ONESIDE_HORDE_TEAM1;
788                mode2 = ONESIDE_HORDE_TEAM2;
789            }
790
791            // create random arena
792            uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL};
793            uint32 arena_num = urand(0,2);
794            BattleGround* bg2 = NULL;
795            if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) &&
796                !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) &&
797                !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) )
798            {
799                sLog.outError("Could not create arena.");
800                return;
801            }
802
803            sLog.outDebug("Battleground: One-faction arena created.");
804            // init stats
805            if(sBattleGroundMgr.isArenaTesting())
806            {
807                bg2->SetMaxPlayersPerTeam(1);
808                bg2->SetMaxPlayers(2);
809            }
810            else
811            {
812                switch(arenatype)
813                {
814                case ARENA_TYPE_2v2:
815                    bg2->SetMaxPlayersPerTeam(2);
816                    bg2->SetMaxPlayers(4);
817                    break;
818                case ARENA_TYPE_3v3:
819                    bg2->SetMaxPlayersPerTeam(3);
820                    bg2->SetMaxPlayers(6);
821                    break;
822                case ARENA_TYPE_5v5:
823                    bg2->SetMaxPlayersPerTeam(5);
824                    bg2->SetMaxPlayers(10);
825                    break;
826                default:
827                    break;
828                }
829            }
830
831            bg2->SetRated(isRated);
832
833            // assigned team of the other group
834            uint32 other_side;
835            if(side == ALLIANCE)
836                other_side = HORDE;
837            else
838                other_side = ALLIANCE;
839
840            // start the joining of the bg
841            bg2->SetStatus(STATUS_WAIT_JOIN);
842            bg2->SetQueueType(queue_id);
843            // initialize arena / rating info
844            bg2->SetArenaType(arenatype);
845
846            std::list<GroupQueueInfo* >::iterator itr;
847
848            // invite players from the first group as horde players (actually green team)
849            for(itr = m_SelectionPools[mode1].SelectedGroups.begin(); itr != m_SelectionPools[mode1].SelectedGroups.end(); ++itr)
850            {
851                InviteGroupToBG((*itr),bg2,HORDE);
852            }
853
854            // invite players from the second group as ally players (actually gold team)
855            for(itr = m_SelectionPools[mode2].SelectedGroups.begin(); itr != m_SelectionPools[mode2].SelectedGroups.end(); ++itr)
856            {
857                InviteGroupToBG((*itr),bg2,ALLIANCE);
858            }
859
860            bg2->StartBattleGround();
861        }
862    }
863}
864
865/*********************************************************/
866/***            BATTLEGROUND QUEUE EVENTS              ***/
867/*********************************************************/
868
869bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 p_time)
870{
871    Player* plr = objmgr.GetPlayer( m_PlayerGuid );
872
873    // player logged off (we should do nothing, he is correctly removed from queue in another procedure)
874    if (!plr)
875        return true;
876
877    // Player can be in another BG queue and must be removed in normal way in any case
878    // // player is already in battleground ... do nothing (battleground queue status is deleted when player is teleported to BG)
879    // if (plr->GetBattleGroundId() > 0)
880    //    return true;
881
882    BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID);
883    if (!bg)
884        return true;
885
886    uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID());
887    if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES)         // player is in queue
888    {
889        uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
890        uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
891        if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue
892        {
893            // 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
894            BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()];
895            BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid);
896            if (qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID)
897            {
898                WorldPacket data;
899                sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, qItr->second.GroupInfo->Team, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME/2, 0);
900                plr->GetSession()->SendPacket(&data);
901            }
902        }
903    }
904    return true;                                            //event will be deleted
905}
906
907void BGQueueInviteEvent::Abort(uint64 /*e_time*/)
908{
909    //this should not be called
910    sLog.outError("Battleground invite event ABORTED!");
911}
912
913bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
914{
915    Player* plr = objmgr.GetPlayer( m_PlayerGuid );
916    if (!plr)
917        // player logged off (we should do nothing, he is correctly removed from queue in another procedure)
918        return true;
919
920    // Player can be in another BG queue and must be removed in normal way in any case
921    //if (plr->InBattleGround())
922    //    // player is already in battleground ... do nothing (battleground queue status is deleted when player is teleported to BG)
923    //    return true;
924
925    BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID);
926    if (!bg)
927        return true;
928
929    sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID);
930
931    uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
932    uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
933    if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue
934    {
935        // 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
936        BattleGroundQueue::QueuedPlayersMap::iterator qMapItr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].find(m_PlayerGuid);
937        if (qMapItr != sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].end() && qMapItr->second.GroupInfo && qMapItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID)
938        {
939            plr->RemoveBattleGroundQueueId(bgQueueTypeId);
940            sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(m_PlayerGuid, true);
941            sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgQueueTypeId, bg->GetQueueType());
942            WorldPacket data;
943            sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, m_PlayersTeam, queueSlot, STATUS_NONE, 0, 0);
944            plr->GetSession()->SendPacket(&data);
945        }
946    }
947    else
948        sLog.outDebug("Battleground: Player was already removed from queue");
949
950    //event will be deleted
951    return true;
952}
953
954void BGQueueRemoveEvent::Abort(uint64 /*e_time*/)
955{
956    //this should not be called
957    sLog.outError("Battleground remove event ABORTED!");
958}
959
960/*********************************************************/
961/***            BATTLEGROUND MANAGER                   ***/
962/*********************************************************/
963
964BattleGroundMgr::BattleGroundMgr()
965{
966    m_BattleGrounds.clear();
967    m_AutoDistributePoints = (bool)sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS);
968    m_MaxRatingDifference = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE);
969    m_RatingDiscardTimer = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER);
970    m_PrematureFinishTimer = sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER);
971    m_NextRatingDiscardUpdate = m_RatingDiscardTimer;
972    m_AutoDistributionTimeChecker = 0;
973    m_ArenaTesting = false;
974}
975
976BattleGroundMgr::~BattleGroundMgr()
977{
978    BattleGroundSet::iterator itr, next;
979    for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next)
980    {
981        next = itr;
982        ++next;
983        BattleGround * bg = itr->second;
984        m_BattleGrounds.erase(itr);
985        delete bg;
986    }
987    m_BattleGrounds.clear();
988}
989
990// used to update running battlegrounds, and delete finished ones
991void BattleGroundMgr::Update(time_t diff)
992{
993    BattleGroundSet::iterator itr, next;
994    for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next)
995    {
996        next = itr;
997        ++next;
998        itr->second->Update(diff);
999        // use the SetDeleteThis variable
1000        // direct deletion caused crashes
1001        if(itr->second->m_SetDeleteThis)
1002        {
1003            BattleGround * bg = itr->second;
1004            m_BattleGrounds.erase(itr);
1005            delete bg;
1006        }
1007    }
1008    // if rating difference counts, maybe force-update queues
1009    if(m_MaxRatingDifference)
1010    {
1011        // it's time to force update
1012        if(m_NextRatingDiscardUpdate < diff)
1013        {
1014            // forced update for level 70 rated arenas
1015            m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA,6,ARENA_TYPE_2v2,true,0);
1016            m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA,6,ARENA_TYPE_3v3,true,0);
1017            m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA,6,ARENA_TYPE_5v5,true,0);
1018            m_NextRatingDiscardUpdate = m_RatingDiscardTimer;
1019        } 
1020        else 
1021            m_NextRatingDiscardUpdate -= diff;
1022    }
1023    if(m_AutoDistributePoints)
1024    {
1025        if(m_AutoDistributionTimeChecker < diff)
1026        {
1027            if(time(NULL) > m_NextAutoDistributionTime)
1028            {
1029                DistributeArenaPoints();
1030                m_NextAutoDistributionTime = time(NULL) + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS);
1031                CharacterDatabase.PExecute("UPDATE saved_variables SET NextArenaPointDistributionTime = FROM_UNIXTIME('"I64FMTD"')",(uint64)m_NextAutoDistributionTime);
1032            }
1033            m_AutoDistributionTimeChecker = 600000; // check 10 minutes
1034        }
1035        else
1036            m_AutoDistributionTimeChecker -= diff;
1037    }
1038}
1039
1040void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype, uint8 israted)
1041{
1042    // we can be in 3 queues in same time...
1043    if(StatusID == 0)
1044    {
1045        data->Initialize(SMSG_BATTLEFIELD_STATUS, 4*3);
1046        *data << uint32(QueueSlot);                         // queue id (0...2)
1047        *data << uint64(0);
1048        return;
1049    }
1050
1051    data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4));
1052    *data << uint32(QueueSlot);                             // queue id (0...2) - player can be in 3 queues in time
1053    // uint64 in client
1054    *data << uint64( uint64(arenatype ? arenatype : bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) );
1055    *data << uint32(0);                                     // unknown
1056    // alliance/horde for BG and skirmish/rated for Arenas
1057    *data << uint8(bg->isArena() ? ( israted ? israted : bg->isRated() ) : bg->GetTeamIndexByTeamId(team));
1058/*    *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!!!!
1059    switch(bg->GetTypeID())                                 // value depends on bg id
1060    {
1061        case BATTLEGROUND_AV:
1062            *data << uint8(1);
1063            break;
1064        case BATTLEGROUND_WS:
1065            *data << uint8(2);
1066            break;
1067        case BATTLEGROUND_AB:
1068            *data << uint8(3);
1069            break;
1070        case BATTLEGROUND_NA:
1071            *data << uint8(4);
1072            break;
1073        case BATTLEGROUND_BE:
1074            *data << uint8(5);
1075            break;
1076        case BATTLEGROUND_AA:
1077            *data << uint8(6);
1078            break;
1079        case BATTLEGROUND_EY:
1080            *data << uint8(7);
1081            break;
1082        case BATTLEGROUND_RL:
1083            *data << uint8(8);
1084            break;
1085        default:                                            // unknown
1086            *data << uint8(0);
1087            break;
1088    }
1089
1090    if(bg->isArena() && (StatusID == STATUS_WAIT_QUEUE))
1091        *data << uint32(BATTLEGROUND_AA);                   // all arenas   I don't think so.
1092    else
1093    *data << uint32(bg->GetTypeID());                   // BG id from DBC
1094
1095    *data << uint16(0x1F90);                                // unk value 8080
1096    *data << uint32(bg->GetInstanceID());                   // instance id
1097
1098    if(bg->isBattleGround())
1099        *data << uint8(bg->GetTeamIndexByTeamId(team));     // team
1100    else
1101        *data << uint8(israted?israted:bg->isRated());                      // is rated battle
1102*/
1103    *data << uint32(StatusID);                              // status
1104    switch(StatusID)
1105    {
1106        case STATUS_WAIT_QUEUE:                             // status_in_queue
1107            *data << uint32(Time1);                         // average wait time, milliseconds
1108            *data << uint32(Time2);                         // time in queue, updated every minute?
1109            break;
1110        case STATUS_WAIT_JOIN:                              // status_invite
1111            *data << uint32(bg->GetMapId());                // map id
1112            *data << uint32(Time1);                         // time to remove from queue, milliseconds
1113            break;
1114        case STATUS_IN_PROGRESS:                            // status_in_progress
1115            *data << uint32(bg->GetMapId());                // map id
1116            *data << uint32(Time1);                         // 0 at bg start, 120000 after bg end, time to bg auto leave, milliseconds
1117            *data << uint32(Time2);                         // time from bg start, milliseconds
1118            *data << uint8(0x1);                            // unk sometimes 0x0!
1119            break;
1120        default:
1121            sLog.outError("Unknown BG status!");
1122            break;
1123    }
1124}
1125
1126void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg)
1127{
1128    uint8 type = (bg->isArena() ? 1 : 0);
1129                                                            // last check on 2.4.1
1130    data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize()));
1131    *data << uint8(type);                                   // seems to be type (battleground=0/arena=1)
1132
1133    if(type)                                                // arena
1134    {
1135        // it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H
1136        for(int i = 1; i >= 0; --i)
1137        {
1138            *data << uint32(3000-bg->m_ArenaTeamRatingChanges[i]);                      // rating change: showed value - 3000
1139            *data << uint32(3999);  // huge thanks for TOM_RUS for this!
1140            sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]);
1141        }
1142        for(int i = 1; i >= 0; --i)
1143        {
1144            uint32 at_id = bg->m_ArenaTeamIds[i];
1145            ArenaTeam * at = objmgr.GetArenaTeamById(at_id);
1146            if(at)
1147                *data << at->GetName();
1148            else//*/
1149                *data << (uint8)0;
1150        }
1151    }
1152
1153    if(bg->GetWinner() == 2)
1154    {
1155        *data << uint8(0);                                  // bg in progress
1156    }
1157    else
1158    {
1159        *data << uint8(1);                                  // bg ended
1160        *data << uint8(bg->GetWinner());                    // who win
1161    }
1162
1163    *data << (int32)(bg->GetPlayerScoresSize());
1164
1165    for(std::map<uint64, BattleGroundScore*>::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr)
1166    {
1167        *data << (uint64)itr->first;
1168        *data << (int32)itr->second->KillingBlows;
1169        Player *plr = objmgr.GetPlayer(itr->first);
1170        uint32 team = bg->GetPlayerTeam(itr->first);
1171        if(!team && plr) team = plr->GetTeam();
1172        if(type == 0)
1173        {
1174            *data << (int32)itr->second->HonorableKills;
1175            *data << (int32)itr->second->Deaths;
1176            *data << (int32)(itr->second->BonusHonor);
1177        }
1178        else
1179        {
1180            // that part probably wrong
1181            if(plr)
1182            {
1183                if(team == HORDE)
1184                    *data << uint8(0);
1185                else if(team == ALLIANCE)
1186                {
1187                    *data << uint8(1);
1188                }
1189                else
1190                    *data << uint8(0);
1191            }
1192            else
1193                *data << uint8(0);
1194        }
1195        *data << (int32)itr->second->DamageDone;             // damage done
1196        *data << (int32)itr->second->HealingDone;            // healing done
1197        switch(bg->GetTypeID())                              // battleground specific things
1198        {
1199            case BATTLEGROUND_AV:
1200                *data << (uint32)0x00000005;                // count of next fields
1201                *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted;  // GraveyardsAssaulted
1202                *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsDefended;   // GraveyardsDefended
1203                *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersAssaulted;      // TowersAssaulted
1204                *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersDefended;       // TowersDefended
1205                *data << (uint32)((BattleGroundAVScore*)itr->second)->MinesCaptured;        // MinesCaptured
1206                break;
1207            case BATTLEGROUND_WS:
1208                *data << (uint32)0x00000002;                // count of next fields
1209                *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagCaptures;         // flag captures
1210                *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagReturns;          // flag returns
1211                break;
1212            case BATTLEGROUND_AB:
1213                *data << (uint32)0x00000002;                // count of next fields
1214                *data << (uint32)((BattleGroundABScore*)itr->second)->BasesAssaulted;       // bases asssulted
1215                *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended;        // bases defended
1216                break;
1217            case BATTLEGROUND_EY:
1218                *data << (uint32)0x00000001;                 // count of next fields
1219                *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures;         // flag captures
1220                break;
1221            case BATTLEGROUND_NA:
1222            case BATTLEGROUND_BE:
1223            case BATTLEGROUND_AA:
1224            case BATTLEGROUND_RL:
1225                *data << (int32)0;                          // 0
1226                break;
1227            default:
1228                sLog.outDebug("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID());
1229                *data << (int32)0;
1230                break;
1231        }
1232    }
1233}
1234
1235void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId)
1236{
1237    /*bgTypeId is:
1238    0 - Your group has joined a battleground queue, but you are not eligible
1239    1 - Your group has joined the queue for AV
1240    2 - Your group has joined the queue for WS
1241    3 - Your group has joined the queue for AB
1242    4 - Your group has joined the queue for NA
1243    5 - Your group has joined the queue for BE Arena
1244    6 - Your group has joined the queue for All Arenas
1245    7 - Your group has joined the queue for EotS*/
1246    data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4);
1247    *data << uint32(bgTypeId);
1248}
1249
1250void BattleGroundMgr::BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value)
1251{
1252    data->Initialize(SMSG_UPDATE_WORLD_STATE, 4+4);
1253    *data << uint32(field);
1254    *data << uint32(value);
1255}
1256
1257void BattleGroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid)
1258{
1259    data->Initialize(SMSG_PLAY_SOUND, 4);
1260    *data << uint32(soundid);
1261}
1262
1263void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, Player *plr)
1264{
1265    data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8);
1266    *data << uint64(plr->GetGUID());
1267}
1268
1269void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr)
1270{
1271    data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8);
1272    *data << uint64(plr->GetGUID());
1273}
1274
1275void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team)
1276{
1277    // set invited player counters:
1278    BattleGround* bg = this->GetBattleGround(bgInstanceGUID);
1279    if(!bg)
1280        return;
1281    bg->IncreaseInvitedCount(team);
1282   
1283    plr->SetInviteForBattleGroundQueueType(BGQueueTypeId(bg->GetTypeID(),bg->GetArenaType()), bgInstanceGUID);
1284
1285    // set the arena teams for rated matches
1286    if(bg->isArena() && bg->isRated())
1287    {
1288        switch(bg->GetArenaType())
1289        {
1290        case ARENA_TYPE_2v2:
1291            bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(0));
1292            break;
1293        case ARENA_TYPE_3v3:
1294            bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(1));
1295            break;
1296        case ARENA_TYPE_5v5:
1297            bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(2));
1298            break;
1299        default:
1300            break;
1301        }
1302    }
1303
1304    // create invite events:
1305    //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
1306    BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), bgInstanceGUID);
1307    plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME/2));
1308    BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, team);
1309    plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME));
1310}
1311
1312BattleGround * BattleGroundMgr::GetBattleGroundTemplate(uint32 bgTypeId)
1313{
1314    return BGFreeSlotQueue[bgTypeId].empty() ? NULL : BGFreeSlotQueue[bgTypeId].back();
1315}
1316
1317// create a new battleground that will really be used to play
1318BattleGround * BattleGroundMgr::CreateNewBattleGround(uint32 bgTypeId)
1319{
1320    BattleGround *bg = NULL;
1321
1322    // get the template BG
1323    BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId);
1324
1325    if(!bg_template)
1326    {
1327        sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId);
1328        return 0;
1329    }
1330   
1331    // create a copy of the BG template
1332    switch(bgTypeId)
1333    {
1334        case BATTLEGROUND_AV: 
1335            bg = new BattleGroundAV(*(BattleGroundAV*)bg_template); 
1336            break;
1337        case BATTLEGROUND_WS: 
1338            bg = new BattleGroundWS(*(BattleGroundWS*)bg_template); 
1339            break;
1340        case BATTLEGROUND_AB: 
1341            bg = new BattleGroundAB(*(BattleGroundAB*)bg_template); 
1342            break;
1343        case BATTLEGROUND_NA: 
1344            bg = new BattleGroundNA(*(BattleGroundNA*)bg_template); 
1345            break;
1346        case BATTLEGROUND_BE: 
1347            bg = new BattleGroundBE(*(BattleGroundBE*)bg_template); 
1348            break;
1349        case BATTLEGROUND_AA: 
1350            bg = new BattleGroundAA(*(BattleGroundAA*)bg_template); 
1351            break;
1352        case BATTLEGROUND_EY: 
1353            bg = new BattleGroundEY(*(BattleGroundEY*)bg_template); 
1354            break;
1355        case BATTLEGROUND_RL: 
1356            bg = new BattleGroundRL(*(BattleGroundRL*)bg_template); 
1357            break;
1358        default:
1359            //bg = new BattleGround;   
1360            return 0;
1361            break;             // placeholder for non implemented BG
1362    }
1363
1364    // generate a new instance id
1365    bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id
1366
1367    // reset the new bg (set status to status_wait_queue from status_none)
1368    bg->Reset();
1369
1370    /*   will be setup in BG::Update() when the first player is ported in
1371    if(!(bg->SetupBattleGround()))
1372    {
1373        sLog.outError("BattleGround: CreateNewBattleGround: SetupBattleGround failed for bg %u", bgTypeId);
1374        delete bg;
1375        return 0;
1376    }
1377    */
1378
1379    // add BG to free slot queue
1380    bg->AddToBGFreeSlotQueue();
1381
1382    // add bg to update list
1383    AddBattleGround(bg->GetInstanceID(), bg);
1384
1385    return bg;
1386}
1387
1388// used to create the BG templates
1389uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, 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)
1390{
1391    // Create the BG
1392    BattleGround *bg = NULL;
1393
1394    switch(bgTypeId)
1395    {
1396        case BATTLEGROUND_AV: bg = new BattleGroundAV; break;
1397        case BATTLEGROUND_WS: bg = new BattleGroundWS; break;
1398        case BATTLEGROUND_AB: bg = new BattleGroundAB; break;
1399        case BATTLEGROUND_NA: bg = new BattleGroundNA; break;
1400        case BATTLEGROUND_BE: bg = new BattleGroundBE; break;
1401        case BATTLEGROUND_AA: bg = new BattleGroundAA; break;
1402        case BATTLEGROUND_EY: bg = new BattleGroundEY; break;
1403        case BATTLEGROUND_RL: bg = new BattleGroundRL; break;
1404        default:bg = new BattleGround;   break;             // placeholder for non implemented BG
1405    }
1406
1407    bg->SetMapId(MapID);
1408
1409    bg->Reset();
1410
1411    BattlemasterListEntry const *bl = sBattlemasterListStore.LookupEntry(bgTypeId);
1412    //in previous method is checked if exists entry in sBattlemasterListStore, so no check needed
1413    if (bl)
1414    {
1415        bg->SetArenaorBGType(bl->type == TYPE_ARENA);
1416    }
1417
1418    bg->SetTypeID(bgTypeId);
1419    bg->SetInstanceID(0);                               // template bg, instance id is 0
1420    bg->SetMinPlayersPerTeam(MinPlayersPerTeam);
1421    bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam);
1422    bg->SetMinPlayers(MinPlayersPerTeam*2);
1423    bg->SetMaxPlayers(MaxPlayersPerTeam*2);
1424    bg->SetName(BattleGroundName);
1425    bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO);
1426    bg->SetTeamStartLoc(HORDE,    Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO);
1427    bg->SetLevelRange(LevelMin, LevelMax);
1428
1429    //add BattleGround instance to FreeSlotQueue (.back() will return the template!)
1430    bg->AddToBGFreeSlotQueue();
1431
1432    // do NOT add to update list, since this is a template battleground!
1433
1434    // return some not-null value, bgTypeId is good enough for me
1435    return bgTypeId; 
1436}
1437
1438void BattleGroundMgr::CreateInitialBattleGrounds()
1439{
1440    float AStartLoc[4];
1441    float HStartLoc[4];
1442    uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2;
1443    BattlemasterListEntry const *bl;
1444    WorldSafeLocsEntry const *start;
1445
1446    uint32 count = 0;
1447
1448    //                                                0   1                 2                 3      4      5                6              7             8
1449    QueryResult *result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO FROM battleground_template");
1450
1451    if(!result)
1452    {
1453        barGoLink bar(1);
1454
1455        bar.step();
1456
1457        sLog.outString();
1458        sLog.outErrorDb(">> Loaded 0 battlegrounds. DB table `battleground_template` is empty.");
1459        return;
1460    }
1461
1462    barGoLink bar(result->GetRowCount());
1463
1464    do
1465    {
1466        Field *fields = result->Fetch();
1467        bar.step();
1468
1469        uint32 bgTypeID = fields[0].GetUInt32();
1470
1471        // can be overwrited by values from DB
1472        bl = sBattlemasterListStore.LookupEntry(bgTypeID);
1473        if(!bl)
1474        {
1475            sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.",bgTypeID);
1476            continue;
1477        }
1478
1479        MaxPlayersPerTeam = bl->maxplayersperteam;
1480        MinPlayersPerTeam = bl->maxplayersperteam/2;
1481        MinLvl = bl->minlvl;
1482        MaxLvl = bl->maxlvl;
1483
1484        if(fields[1].GetUInt32())
1485            MinPlayersPerTeam = fields[1].GetUInt32();
1486
1487        if(fields[2].GetUInt32())
1488            MaxPlayersPerTeam = fields[2].GetUInt32();
1489
1490        if(fields[3].GetUInt32())
1491            MinLvl = fields[3].GetUInt32();
1492
1493        if(fields[4].GetUInt32())
1494            MaxLvl = fields[4].GetUInt32();
1495
1496        start1 = fields[5].GetUInt32();
1497
1498        start = sWorldSafeLocsStore.LookupEntry(start1);
1499        if(start)
1500        {
1501            AStartLoc[0] = start->x;
1502            AStartLoc[1] = start->y;
1503            AStartLoc[2] = start->z;
1504            AStartLoc[3] = fields[6].GetFloat();
1505        }
1506        else if(bgTypeID == BATTLEGROUND_AA)
1507        {
1508            AStartLoc[0] = 0;
1509            AStartLoc[1] = 0;
1510            AStartLoc[2] = 0;
1511            AStartLoc[3] = fields[6].GetFloat();
1512        }
1513        else
1514        {
1515            sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.",bgTypeID,start1);
1516            continue;
1517        }
1518
1519        start2 = fields[7].GetUInt32();
1520
1521        start = sWorldSafeLocsStore.LookupEntry(start2);
1522        if(start)
1523        {
1524            HStartLoc[0] = start->x;
1525            HStartLoc[1] = start->y;
1526            HStartLoc[2] = start->z;
1527            HStartLoc[3] = fields[8].GetFloat();
1528        }
1529        else if(bgTypeID == BATTLEGROUND_AA)
1530        {
1531            HStartLoc[0] = 0;
1532            HStartLoc[1] = 0;
1533            HStartLoc[2] = 0;
1534            HStartLoc[3] = fields[8].GetFloat();
1535        }
1536        else
1537        {
1538            sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.",bgTypeID,start2);
1539            continue;
1540        }
1541
1542        //sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl);
1543        if(!CreateBattleGround(bgTypeID, 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]))
1544            continue;
1545
1546        ++count;
1547    } while (result->NextRow());
1548
1549    delete result;
1550
1551    sLog.outString();
1552    sLog.outString( ">> Loaded %u battlegrounds", count );
1553}
1554
1555void BattleGroundMgr::InitAutomaticArenaPointDistribution()
1556{
1557    if(m_AutoDistributePoints)
1558    {
1559        sLog.outDebug("Initializing Automatic Arena Point Distribution");
1560        QueryResult * result = CharacterDatabase.Query("SELECT UNIX_TIMESTAMP(NextArenaPointDistributionTime) FROM saved_variables");
1561        if(!result)
1562        {
1563            sLog.outDebug("Battleground: Next arena point distribution time not found in SavedVariables, reseting it now."); 
1564            m_NextAutoDistributionTime = time(NULL) + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS);
1565            CharacterDatabase.PExecute("INSERT INTO saved_variables (NextArenaPointDistributionTime) VALUES ( FROM_UNIXTIME('"I64FMTD"') )",(uint64)m_NextAutoDistributionTime);
1566        }
1567        else
1568        {
1569            m_NextAutoDistributionTime = (*result)[0].GetUInt64();
1570            delete result;
1571        }
1572        sLog.outDebug("Automatic Arena Point Distribution initialized.");
1573    }
1574}
1575
1576void BattleGroundMgr::DistributeArenaPoints()
1577{
1578    // used to distribute arena points based on last week's stats
1579
1580    CharacterDatabase.BeginTransaction();
1581    // direct execute, because of the later GetUInt32ValueFromDB() calls
1582                                                                                                                                        // 1                                                                                               2                                                          3                                                 4                                                                                                                                      5                                                                              6                                                         7                                                 8                                                                                                                                  9                                                                                    10                                                 1                           2                              3                        4                                        5                                      6                        7                              8                       9                                             10
1583    CharacterDatabase.DirectPExecute("UPDATE characters b, arena_team_member a SET b.data = CONCAT( SUBSTRING_INDEX(b.data, ' ', '%u'),' ', CAST( IF ( ((CAST( SUBSTRING( b.data FROM (CHAR_LENGTH(SUBSTRING_INDEX(b.data,' ','%u')) + 2) FOR (CHAR_LENGTH(SUBSTRING_INDEX(b.data,' ','%u')) - CHAR_LENGTH(SUBSTRING_INDEX(b.data,' ','%u')) - 1) ) AS UNSIGNED) + (SELECT MAX(c.points_to_add) FROM arena_team_member c WHERE c.guid = b.guid GROUP BY c.guid) ) < '%u'), CAST(SUBSTRING(b.data FROM (CHAR_LENGTH(SUBSTRING_INDEX(b.data,' ','%u')) + 2) FOR (CHAR_LENGTH(SUBSTRING_INDEX(b.data,' ','%u')) - CHAR_LENGTH(SUBSTRING_INDEX(b.data,' ','%u')) - 1) ) AS UNSIGNED) + (SELECT MAX(d.points_to_add) FROM arena_team_member d WHERE d.guid = b.guid GROUP BY d.guid), '%u') AS CHAR),' ',SUBSTRING(b.data FROM (CHAR_LENGTH(SUBSTRING_INDEX(b.data,' ','%u')) + 2))) WHERE b.guid = a.guid",PLAYER_FIELD_ARENA_CURRENCY, PLAYER_FIELD_ARENA_CURRENCY, PLAYER_FIELD_ARENA_CURRENCY+1, PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_MAX_ARENA_POINTS),PLAYER_FIELD_ARENA_CURRENCY, PLAYER_FIELD_ARENA_CURRENCY+1, PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_MAX_ARENA_POINTS), PLAYER_FIELD_ARENA_CURRENCY+1);
1584    for(int i=0; i<3; ++i)
1585    {
1586        // reset weekly played matches
1587        uint32 position = PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * i + 2;
1588        CharacterDatabase.DirectPExecute("UPDATE characters SET data = CONCAT( SUBSTRING_INDEX(data,' ','%u'),' ','0',' ',SUBSTRING(data FROM (CHAR_LENGTH(SUBSTRING_INDEX(data,' ','%u')) + 2)))",position, position + 1);
1589    }
1590    CharacterDatabase.DirectExecute("UPDATE arena_team_member SET points_to_add = '0', played_week = '0', wons_week = '0'");
1591    CharacterDatabase.DirectExecute("UPDATE arena_team_stats SET games = '0', wins = '0'");
1592    CharacterDatabase.CommitTransaction();
1593
1594    QueryResult *result = CharacterDatabase.PQuery("SELECT guid, data FROM characters WHERE online = '1'");
1595    if( result )
1596    {
1597        do
1598        {
1599            Field *fields = result->Fetch();
1600
1601            uint32 guid = fields[0].GetUInt32();
1602            if(Player * pl = objmgr.GetPlayer(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER)))
1603            {
1604                Tokens data = StrSplit(fields[1].GetCppString(), " ");
1605                // update arena currency
1606                pl->SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, Player::GetUInt32ValueFromArray(data, PLAYER_FIELD_ARENA_CURRENCY));
1607                // reset played this week count for all teams
1608                for(int i= 0; i < 3; ++i)
1609                    pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * i + 2, 0);
1610            }
1611
1612        } while (result->NextRow());
1613
1614        delete result;
1615    }
1616
1617    for(ObjectMgr::ArenaTeamSet::iterator titr = objmgr.GetArenaTeamSetBegin(); titr != objmgr.GetArenaTeamSetEnd(); ++titr)
1618    {
1619        if(ArenaTeam * at = (*titr))
1620        {
1621            at->FinishWeek();   // set played this week etc values to 0 in memory, too
1622            // at->SaveToDB(); // no need, the modified values are already saved above
1623            at->NotifyStatsChanged();  // notify the players of the changes
1624        }
1625    }
1626}
1627
1628void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player* plr, uint32 bgTypeId)
1629{
1630    uint32 PlayerLevel = 10;
1631
1632    if(plr)
1633        PlayerLevel = plr->getLevel();
1634
1635    data->Initialize(SMSG_BATTLEFIELD_LIST);
1636    *data << uint64(guid);                                  // battlemaster guid
1637    *data << uint32(bgTypeId);                              // battleground id
1638    if(bgTypeId == BATTLEGROUND_AA)                         // arena
1639    {
1640        *data << uint8(5);                                  // unk
1641        *data << uint32(0);                                 // unk
1642    }
1643    else                                                    // battleground
1644    {
1645        *data << uint8(0x00);                               // unk
1646
1647        size_t count_pos = data->wpos();
1648        uint32 count = 0;
1649        *data << uint32(0x00);                              // number of bg instances
1650
1651        for(std::map<uint32, BattleGround*>::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr)
1652        {
1653            if(itr->second->GetTypeID() == bgTypeId && (PlayerLevel >= itr->second->GetMinLevel()) && (PlayerLevel <= itr->second->GetMaxLevel()))
1654            {
1655                *data << uint32(itr->second->GetInstanceID());
1656                ++count;
1657            }
1658        }
1659        data->put<uint32>( count_pos , count);
1660    }
1661}
1662
1663void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId)
1664{
1665    BattleGround *bg = GetBattleGround(instanceId);
1666    if(bg)
1667    {
1668        uint32 mapid = bg->GetMapId();
1669        float x, y, z, O;
1670        uint32 team = pl->GetBGTeam();
1671        if(team==0)
1672            team = pl->GetTeam();
1673        bg->GetTeamStartLoc(team, x, y, z, O);
1674
1675        sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O);
1676        pl->TeleportTo(mapid, x, y, z, O);
1677    }
1678    else
1679    {
1680        sLog.outError("player %u trying to port to non-existent bg instance %u",pl->GetGUIDLow(), instanceId);
1681    }
1682}
1683
1684void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid)
1685{
1686    WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12);
1687    uint32 time_ = 30000 - bg->GetLastResurrectTime();      // resurrect every 30 seconds
1688    if(time_ == uint32(-1))
1689        time_ = 0;
1690    data << guid << time_;
1691    pl->GetSession()->SendPacket(&data);
1692}
1693
1694void BattleGroundMgr::RemoveBattleGround(uint32 instanceID)
1695{
1696    BattleGroundSet::iterator itr = m_BattleGrounds.find(instanceID);
1697    if(itr!=m_BattleGrounds.end())
1698        m_BattleGrounds.erase(itr);
1699}
1700
1701bool BattleGroundMgr::IsArenaType(uint32 bgTypeId) const
1702{
1703    return ( bgTypeId == BATTLEGROUND_AA ||
1704        bgTypeId == BATTLEGROUND_BE ||
1705        bgTypeId == BATTLEGROUND_NA ||
1706        bgTypeId == BATTLEGROUND_RL );
1707}
1708
1709bool BattleGroundMgr::IsBattleGroundType(uint32 bgTypeId) const
1710{
1711    return !IsArenaType(bgTypeId);
1712}
1713
1714uint32 BattleGroundMgr::BGQueueTypeId(uint32 bgTypeId, uint8 arenaType) const
1715{
1716    switch(bgTypeId)
1717    {
1718    case BATTLEGROUND_WS:
1719        return BATTLEGROUND_QUEUE_WS;
1720    case BATTLEGROUND_AB:
1721        return BATTLEGROUND_QUEUE_AB;
1722    case BATTLEGROUND_AV:
1723        return BATTLEGROUND_QUEUE_AV;
1724    case BATTLEGROUND_EY:
1725        return BATTLEGROUND_QUEUE_EY;
1726    case BATTLEGROUND_AA:
1727    case BATTLEGROUND_NA:
1728    case BATTLEGROUND_RL:
1729    case BATTLEGROUND_BE:
1730        switch(arenaType)
1731        {
1732        case ARENA_TYPE_2v2:
1733            return BATTLEGROUND_QUEUE_2v2;
1734        case ARENA_TYPE_3v3:
1735            return BATTLEGROUND_QUEUE_3v3;
1736        case ARENA_TYPE_5v5:
1737            return BATTLEGROUND_QUEUE_5v5;
1738        default:
1739            return 0;
1740        }
1741    default:
1742        return 0;
1743    }
1744}
1745
1746uint32 BattleGroundMgr::BGTemplateId(uint32 bgQueueTypeId) const
1747{
1748    switch(bgQueueTypeId)
1749    {
1750    case BATTLEGROUND_QUEUE_WS:
1751        return BATTLEGROUND_WS;
1752    case BATTLEGROUND_QUEUE_AB:
1753        return BATTLEGROUND_AB;
1754    case BATTLEGROUND_QUEUE_AV:
1755        return BATTLEGROUND_AV;
1756    case BATTLEGROUND_QUEUE_EY:
1757        return BATTLEGROUND_EY;
1758    case BATTLEGROUND_QUEUE_2v2:
1759    case BATTLEGROUND_QUEUE_3v3:
1760    case BATTLEGROUND_QUEUE_5v5:
1761        return BATTLEGROUND_AA;
1762    default:
1763        return 0;
1764    }
1765}
1766
1767uint8 BattleGroundMgr::BGArenaType(uint32 bgQueueTypeId) const
1768{
1769    switch(bgQueueTypeId)
1770    {
1771    case BATTLEGROUND_QUEUE_2v2:
1772        return ARENA_TYPE_2v2;
1773    case BATTLEGROUND_QUEUE_3v3:
1774        return ARENA_TYPE_3v3;
1775    case BATTLEGROUND_QUEUE_5v5:
1776        return ARENA_TYPE_5v5;
1777    default:
1778        return 0;
1779    }
1780}
1781
1782void BattleGroundMgr::ToggleArenaTesting()
1783{
1784    m_ArenaTesting = !m_ArenaTesting;
1785    sWorld.SendWorldText(LANG_ARENA_TESTING, m_ArenaTesting ? "on" : "off");
1786}
Note: See TracBrowser for help on using the browser.