root/trunk/src/game/Group.cpp @ 9

Revision 9, 49.4 kB (checked in by yumileroy, 17 years ago)

[svn] -enabled instantiated battlegrounds
-enabled arena matches
-rewritten battleground queuing to support joining as group
-removed queue announcements

Original author: w12x
Date: 2008-10-05 08:48:32-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
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.
8 *
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.
13 *
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
17 */
18
19#include "Common.h"
20#include "Opcodes.h"
21#include "WorldPacket.h"
22#include "WorldSession.h"
23#include "Player.h"
24#include "World.h"
25#include "ObjectMgr.h"
26#include "Group.h"
27#include "ObjectAccessor.h"
28#include "BattleGround.h"
29#include "MapManager.h"
30#include "InstanceSaveMgr.h"
31#include "MapInstanced.h"
32#include "Util.h"
33
34Group::Group()
35{
36    m_leaderGuid        = 0;
37    m_mainTank          = 0;
38    m_mainAssistant     = 0;
39    m_groupType         = (GroupType)0;
40    m_bgGroup           = NULL;
41    m_lootMethod        = (LootMethod)0;
42    m_looterGuid        = 0;
43    m_lootThreshold     = ITEM_QUALITY_UNCOMMON;
44
45    for(int i=0; i<TARGETICONCOUNT; i++)
46        m_targetIcons[i] = 0;
47}
48
49Group::~Group()
50{
51    if(m_bgGroup)
52    {
53        sLog.outDebug("Group::~Group: battleground group being deleted.");
54        if(m_bgGroup->GetBgRaid(ALLIANCE) == this) m_bgGroup->SetBgRaid(ALLIANCE, NULL);
55        else if(m_bgGroup->GetBgRaid(HORDE) == this) m_bgGroup->SetBgRaid(HORDE, NULL);
56        else sLog.outError("Group::~Group: battleground group is not linked to the correct battleground.");
57    }
58    Rolls::iterator itr;
59    while(!RollId.empty())
60    {
61        itr = RollId.begin();
62        Roll *r = *itr;
63        RollId.erase(itr);
64        delete(r);
65    }
66
67    // it is undefined whether objectmgr (which stores the groups) or instancesavemgr
68    // will be unloaded first so we must be prepared for both cases
69    // this may unload some instance saves
70    for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
71        for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
72            itr->second.save->RemoveGroup(this);
73}
74
75bool Group::Create(const uint64 &guid, const char * name)
76{
77    m_leaderGuid = guid;
78    m_leaderName = name;
79
80    m_groupType  = isBGGroup() ? GROUPTYPE_RAID : GROUPTYPE_NORMAL;
81    m_lootMethod = GROUP_LOOT;
82    m_lootThreshold = ITEM_QUALITY_UNCOMMON;
83    m_looterGuid = guid;
84
85    m_difficulty = DIFFICULTY_NORMAL;
86    if(!isBGGroup())
87    {
88        Player *leader = objmgr.GetPlayer(guid);
89        if(leader) m_difficulty = leader->GetDifficulty();
90
91        Player::ConvertInstancesToGroup(leader, this, guid);
92
93        // store group in database
94        CharacterDatabase.BeginTransaction();
95        CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid));
96        CharacterDatabase.PExecute("DELETE FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid));
97        CharacterDatabase.PExecute("INSERT INTO groups(leaderGuid,mainTank,mainAssistant,lootMethod,looterGuid,lootThreshold,icon1,icon2,icon3,icon4,icon5,icon6,icon7,icon8,isRaid,difficulty) "
98            "VALUES('%u','%u','%u','%u','%u','%u','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','%u','%u')",
99            GUID_LOPART(m_leaderGuid), GUID_LOPART(m_mainTank), GUID_LOPART(m_mainAssistant), uint32(m_lootMethod),
100            GUID_LOPART(m_looterGuid), uint32(m_lootThreshold), m_targetIcons[0], m_targetIcons[1], m_targetIcons[2], m_targetIcons[3], m_targetIcons[4], m_targetIcons[5], m_targetIcons[6], m_targetIcons[7], isRaidGroup(), m_difficulty);
101    }
102
103    if(!AddMember(guid, name))
104        return false;
105
106    if(!isBGGroup()) CharacterDatabase.CommitTransaction();
107
108    return true;
109}
110
111bool Group::LoadGroupFromDB(const uint64 &leaderGuid, QueryResult *result, bool loadMembers)
112{
113    if(isBGGroup())
114        return false;
115
116    bool external = true;
117    if(!result)
118    {
119        external = false;
120        //                                       0          1              2           3           4              5      6      7      8      9      10     11     12     13      14
121        result = CharacterDatabase.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid));
122        if(!result)
123            return false;
124    }
125
126    m_leaderGuid = leaderGuid;
127
128    // group leader not exist
129    if(!objmgr.GetPlayerNameByGUID(m_leaderGuid, m_leaderName))
130    {
131        if(!external) delete result;
132        return false;
133    }
134
135    m_groupType  = (*result)[13].GetBool() ? GROUPTYPE_RAID : GROUPTYPE_NORMAL;
136    m_difficulty = (*result)[14].GetUInt8();
137    m_mainTank = (*result)[0].GetUInt64();
138    m_mainAssistant = (*result)[1].GetUInt64();
139    m_lootMethod = (LootMethod)(*result)[2].GetUInt8();
140    m_looterGuid = MAKE_NEW_GUID((*result)[3].GetUInt32(), 0, HIGHGUID_PLAYER);
141    m_lootThreshold = (ItemQualities)(*result)[4].GetUInt16();
142
143    for(int i=0; i<TARGETICONCOUNT; i++)
144        m_targetIcons[i] = (*result)[5+i].GetUInt64();
145    if(!external) delete result;
146
147    if(loadMembers)
148    {
149        result = CharacterDatabase.PQuery("SELECT memberGuid, assistant, subgroup FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid));
150        if(!result)
151            return false;
152
153        do
154        {
155            LoadMemberFromDB((*result)[0].GetUInt32(), (*result)[2].GetUInt8(), (*result)[1].GetBool());
156        } while( result->NextRow() );
157        delete result;
158        // group too small
159        if(GetMembersCount() < 2)
160            return false;
161    }
162
163    return true;
164}
165
166bool Group::LoadMemberFromDB(uint32 guidLow, uint8 subgroup, bool assistant)
167{
168    MemberSlot member;
169    member.guid      = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER);
170
171    // skip non-existed member
172    if(!objmgr.GetPlayerNameByGUID(member.guid, member.name))
173        return false;
174
175    member.group     = subgroup;
176    member.assistant = assistant;
177    m_memberSlots.push_back(member);
178    return true;
179}
180
181bool Group::AddInvite(Player *player)
182{
183    if(!player || player->GetGroupInvite() || player->GetGroup())
184        return false;
185
186    RemoveInvite(player);
187
188    m_invitees.insert(player->GetGUID());
189
190    player->SetGroupInvite(this);
191
192    return true;
193}
194
195bool Group::AddLeaderInvite(Player *player)
196{
197    if(!AddInvite(player))
198        return false;
199
200    m_leaderGuid = player->GetGUID();
201    m_leaderName = player->GetName();
202    return true;
203}
204
205uint32 Group::RemoveInvite(Player *player)
206{
207    for(InvitesList::iterator itr=m_invitees.begin(); itr!=m_invitees.end(); ++itr)
208    {
209        if((*itr) == player->GetGUID())
210        {
211            m_invitees.erase(itr);
212            break;
213        }
214    }
215
216    player->SetGroupInvite(NULL);
217    return GetMembersCount();
218}
219
220void Group::RemoveAllInvites()
221{
222    for(InvitesList::iterator itr=m_invitees.begin(); itr!=m_invitees.end(); ++itr)
223    {
224        Player *invitee = objmgr.GetPlayer(*itr);
225        if(invitee)
226            invitee->SetGroupInvite(NULL);
227    }
228    m_invitees.clear();
229}
230
231bool Group::AddMember(const uint64 &guid, const char* name)
232{
233    if(!_addMember(guid, name))
234        return false;
235    SendUpdate();
236
237    Player *player = objmgr.GetPlayer(guid);
238    if(player)
239    {
240        if(!IsLeader(player->GetGUID()) && !isBGGroup())
241        {
242            // reset the new member's instances, unless he is currently in one of them
243            // including raid/heroic instances that they are not permanently bound to!
244            player->ResetInstances(INSTANCE_RESET_GROUP_JOIN);
245
246            if(player->getLevel() >= LEVELREQUIREMENT_HEROIC && player->GetDifficulty() != GetDifficulty() )
247            {
248                player->SetDifficulty(m_difficulty);
249                player->SendDungeonDifficulty(true);
250            }
251        }
252        player->SetGroupUpdateFlag(GROUP_UPDATE_FULL);
253        UpdatePlayerOutOfRange(player);
254    }
255
256    return true;
257}
258
259uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method)
260{
261    // remove member and change leader (if need) only if strong more 2 members _before_ member remove
262    if(GetMembersCount() > (isBGGroup() ? 1 : 2))           // in BG group case allow 1 members group
263    {
264        bool leaderChanged = _removeMember(guid);
265
266        Player *player = objmgr.GetPlayer( guid );
267        if (player)
268        {
269            WorldPacket data;
270
271            if(method == 1)
272            {
273                data.Initialize( SMSG_GROUP_UNINVITE, 0 );
274                player->GetSession()->SendPacket( &data );
275            }
276
277            data.Initialize(SMSG_GROUP_LIST, 24);
278            data << uint64(0) << uint64(0) << uint64(0);
279            player->GetSession()->SendPacket(&data);
280
281            _homebindIfInstance(player);
282        }
283
284        if(leaderChanged)
285        {
286            WorldPacket data(SMSG_GROUP_SET_LEADER, (m_memberSlots.front().name.size()+1));
287            data << m_memberSlots.front().name;
288            BroadcastPacket(&data);
289        }
290
291        SendUpdate();
292    }
293    // if group before remove <= 2 disband it
294    else
295        Disband(true);
296
297    return m_memberSlots.size();
298}
299
300void Group::ChangeLeader(const uint64 &guid)
301{
302    member_citerator slot = _getMemberCSlot(guid);
303
304    if(slot==m_memberSlots.end())
305        return;
306
307    _setLeader(guid);
308
309    WorldPacket data(SMSG_GROUP_SET_LEADER, slot->name.size()+1);
310    data << slot->name;
311    BroadcastPacket(&data);
312    SendUpdate();
313}
314
315void Group::Disband(bool hideDestroy)
316{
317    Player *player;
318
319    for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
320    {
321        player = objmgr.GetPlayer(citr->guid);
322        if(!player)
323            continue;
324
325        player->SetGroup(NULL);
326
327        if(!player->GetSession())
328            continue;
329
330        WorldPacket data;
331        if(!hideDestroy)
332        {
333            data.Initialize(SMSG_GROUP_DESTROYED, 0);
334            player->GetSession()->SendPacket(&data);
335        }
336
337        data.Initialize(SMSG_GROUP_LIST, 24);
338        data << uint64(0) << uint64(0) << uint64(0);
339        player->GetSession()->SendPacket(&data);
340
341        _homebindIfInstance(player);
342    }
343    RollId.clear();
344    m_memberSlots.clear();
345
346    RemoveAllInvites();
347
348    if(!isBGGroup())
349    {
350        CharacterDatabase.BeginTransaction();
351        CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
352        CharacterDatabase.PExecute("DELETE FROM group_member WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
353        CharacterDatabase.CommitTransaction();
354        ResetInstances(INSTANCE_RESET_GROUP_DISBAND, NULL);
355    }
356
357    m_leaderGuid = 0;
358    m_leaderName = "";
359}
360
361/*********************************************************/
362/***                   LOOT SYSTEM                     ***/
363/*********************************************************/
364
365void Group::SendLootStartRoll(uint32 CountDown, const Roll &r)
366{
367    WorldPacket data(SMSG_LOOT_START_ROLL, (8+4+4+4+4+4));
368    data << uint64(r.itemGUID);                             // guid of rolled item
369    data << uint32(r.totalPlayersRolling);                  // maybe the number of players rolling for it???
370    data << uint32(r.itemid);                               // the itemEntryId for the item that shall be rolled for
371    data << uint32(r.itemRandomSuffix);                     // randomSuffix
372    data << uint32(r.itemRandomPropId);                     // item random property ID
373    data << uint32(CountDown);                              // the countdown time to choose "need" or "greed"
374
375    for (Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
376    {
377        Player *p = objmgr.GetPlayer(itr->first);
378        if(!p || !p->GetSession())
379            continue;
380
381        if(itr->second != NOT_VALID)
382            p->GetSession()->SendPacket( &data );
383    }
384}
385
386void Group::SendLootRoll(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r)
387{
388    WorldPacket data(SMSG_LOOT_ROLL, (8+4+8+4+4+4+1+1));
389    data << uint64(SourceGuid);                             // guid of the item rolled
390    data << uint32(0);                                      // unknown, maybe amount of players
391    data << uint64(TargetGuid);
392    data << uint32(r.itemid);                               // the itemEntryId for the item that shall be rolled for
393    data << uint32(r.itemRandomSuffix);                     // randomSuffix
394    data << uint32(r.itemRandomPropId);                     // Item random property ID
395    data << uint8(RollNumber);                              // 0: "Need for: [item name]" > 127: "you passed on: [item name]"      Roll number
396    data << uint8(RollType);                                // 0: "Need for: [item name]" 0: "You have selected need for [item name] 1: need roll 2: greed roll
397    data << uint8(0);                                       // 2.4.0
398
399    for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
400    {
401        Player *p = objmgr.GetPlayer(itr->first);
402        if(!p || !p->GetSession())
403            continue;
404
405        if(itr->second != NOT_VALID)
406            p->GetSession()->SendPacket( &data );
407    }
408}
409
410void Group::SendLootRollWon(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r)
411{
412    WorldPacket data(SMSG_LOOT_ROLL_WON, (8+4+4+4+4+8+1+1));
413    data << uint64(SourceGuid);                             // guid of the item rolled
414    data << uint32(0);                                      // unknown, maybe amount of players
415    data << uint32(r.itemid);                               // the itemEntryId for the item that shall be rolled for
416    data << uint32(r.itemRandomSuffix);                     // randomSuffix
417    data << uint32(r.itemRandomPropId);                     // Item random property
418    data << uint64(TargetGuid);                             // guid of the player who won.
419    data << uint8(RollNumber);                              // rollnumber realted to SMSG_LOOT_ROLL
420    data << uint8(RollType);                                // Rolltype related to SMSG_LOOT_ROLL
421
422    for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
423    {
424        Player *p = objmgr.GetPlayer(itr->first);
425        if(!p || !p->GetSession())
426            continue;
427
428        if(itr->second != NOT_VALID)
429            p->GetSession()->SendPacket( &data );
430    }
431}
432
433void Group::SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r)
434{
435    WorldPacket data(SMSG_LOOT_ALL_PASSED, (8+4+4+4+4));
436    data << uint64(r.itemGUID);                             // Guid of the item rolled
437    data << uint32(NumberOfPlayers);                        // The number of players rolling for it???
438    data << uint32(r.itemid);                               // The itemEntryId for the item that shall be rolled for
439    data << uint32(r.itemRandomPropId);                     // Item random property ID
440    data << uint32(r.itemRandomSuffix);                     // Item random suffix ID
441
442    for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
443    {
444        Player *p = objmgr.GetPlayer(itr->first);
445        if(!p || !p->GetSession())
446            continue;
447
448        if(itr->second != NOT_VALID)
449            p->GetSession()->SendPacket( &data );
450    }
451}
452
453void Group::GroupLoot(uint64 playerGUID, Loot *loot, Creature *creature)
454{
455    std::vector<LootItem>::iterator i;
456    ItemPrototype const *item;
457    uint8 itemSlot = 0;
458    Player *player = objmgr.GetPlayer(playerGUID);
459    Group *group = player->GetGroup();
460
461    for (i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
462    {
463        item = objmgr.GetItemPrototype(i->itemid);
464        if (!item)
465        {
466            //sLog.outDebug("Group::GroupLoot: missing item prototype for item with id: %d", i->itemid);
467            continue;
468        }
469
470        //roll for over-threshold item if it's one-player loot
471        if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall)
472        {
473            uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
474            Roll* r=new Roll(newitemGUID,*i);
475
476            //a vector is filled with only near party members
477            for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
478            {
479                Player *member = itr->getSource();
480                if(!member || !member->GetSession())
481                    continue;
482                if ( i->AllowedForPlayer(member) )
483                {
484                    if (member->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
485                    {
486                        r->playerVote[member->GetGUID()] = NOT_EMITED_YET;
487                        ++r->totalPlayersRolling;
488                    }
489                }
490            }
491
492            r->setLoot(loot);
493            r->itemSlot = itemSlot;
494
495            group->SendLootStartRoll(60000, *r);
496
497            loot->items[itemSlot].is_blocked = true;
498            creature->m_groupLootTimer = 60000;
499            creature->lootingGroupLeaderGUID = GetLeaderGUID();
500
501            RollId.push_back(r);
502        }
503        else
504            i->is_underthreshold=1;
505
506    }
507}
508
509void Group::NeedBeforeGreed(uint64 playerGUID, Loot *loot, Creature *creature)
510{
511    ItemPrototype const *item;
512    Player *player = objmgr.GetPlayer(playerGUID);
513    Group *group = player->GetGroup();
514
515    uint8 itemSlot = 0;
516    for(std::vector<LootItem>::iterator i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
517    {
518        item = objmgr.GetItemPrototype(i->itemid);
519
520        //only roll for one-player items, not for ones everyone can get
521        if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall)
522        {
523            uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
524            Roll* r=new Roll(newitemGUID,*i);
525
526            for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
527            {
528                Player *playerToRoll = itr->getSource();
529                if(!playerToRoll || !playerToRoll->GetSession())
530                    continue;
531
532                if (playerToRoll->CanUseItem(item) && i->AllowedForPlayer(playerToRoll) )
533                {
534                    if (playerToRoll->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
535                    {
536                        r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET;
537                        ++r->totalPlayersRolling;
538                    }
539                }
540            }
541
542            if (r->totalPlayersRolling > 0)
543            {
544                r->setLoot(loot);
545                r->itemSlot = itemSlot;
546
547                group->SendLootStartRoll(60000, *r);
548
549                loot->items[itemSlot].is_blocked = true;
550
551                RollId.push_back(r);
552            }
553            else
554            {
555                delete r;
556            }
557        }
558        else
559            i->is_underthreshold=1;
560    }
561}
562
563void Group::MasterLoot(uint64 playerGUID, Loot* /*loot*/, Creature *creature)
564{
565    Player *player = objmgr.GetPlayer(playerGUID);
566    if(!player)
567        return;
568
569    sLog.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330) player = [%s].", player->GetName());
570
571    uint32 real_count = 0;
572
573    WorldPacket data(SMSG_LOOT_MASTER_LIST, 330);
574    data << (uint8)GetMembersCount();
575
576    for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
577    {
578        Player *looter = itr->getSource();
579        if (!looter->IsInWorld())
580            continue;
581
582        if (looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
583        {
584            data << looter->GetGUID();
585            ++real_count;
586        }
587    }
588
589    data.put<uint8>(0,real_count);
590
591    for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
592    {
593        Player *looter = itr->getSource();
594        if (looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
595            looter->GetSession()->SendPacket(&data);
596    }
597}
598
599void Group::CountRollVote(uint64 playerGUID, uint64 Guid, uint32 NumberOfPlayers, uint8 Choise)
600{
601    Rolls::iterator rollI = GetRoll(Guid);
602    if (rollI == RollId.end())
603        return;
604    Roll* roll = *rollI;
605
606    Roll::PlayerVote::iterator itr = roll->playerVote.find(playerGUID);
607    // this condition means that player joins to the party after roll begins
608    if (itr == roll->playerVote.end())
609        return;
610
611    if (roll->getLoot())
612        if (roll->getLoot()->items.empty())
613            return;
614
615    switch (Choise)
616    {
617        case 0:                                             //Player choose pass
618        {
619            SendLootRoll(0, playerGUID, 128, 128, *roll);
620            ++roll->totalPass;
621            itr->second = PASS;
622        }
623        break;
624        case 1:                                             //player choose Need
625        {
626            SendLootRoll(0, playerGUID, 1, 1, *roll);
627            ++roll->totalNeed;
628            itr->second = NEED;
629        }
630        break;
631        case 2:                                             //player choose Greed
632        {
633            SendLootRoll(0, playerGUID, 2, 2, *roll);
634            ++roll->totalGreed;
635            itr->second = GREED;
636        }
637        break;
638    }
639    if (roll->totalPass + roll->totalGreed + roll->totalNeed >= roll->totalPlayersRolling)
640    {
641        CountTheRoll(rollI, NumberOfPlayers);
642    }
643}
644
645//called when roll timer expires
646void Group::EndRoll()
647{
648    Rolls::iterator itr;
649    while(!RollId.empty())
650    {
651        //need more testing here, if rolls disappear
652        itr = RollId.begin();
653        CountTheRoll(itr, GetMembersCount());               //i don't have to edit player votes, who didn't vote ... he will pass
654    }
655}
656
657void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
658{
659    Roll* roll = *rollI;
660    if(!roll->isValid())                                    // is loot already deleted ?
661    {
662        RollId.erase(rollI);
663        delete roll;
664        return;
665    }
666    //end of the roll
667    if (roll->totalNeed > 0)
668    {
669        if(!roll->playerVote.empty())
670        {
671            uint8 maxresul = 0;
672            uint64 maxguid  = (*roll->playerVote.begin()).first;
673            Player *player;
674
675            for( Roll::PlayerVote::const_iterator itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr)
676            {
677                if (itr->second != NEED)
678                    continue;
679
680                uint8 randomN = urand(1, 99);
681                SendLootRoll(0, itr->first, randomN, 1, *roll);
682                if (maxresul < randomN)
683                {
684                    maxguid  = itr->first;
685                    maxresul = randomN;
686                }
687            }
688            SendLootRollWon(0, maxguid, maxresul, 1, *roll);
689            player = objmgr.GetPlayer(maxguid);
690
691            if(player && player->GetSession())
692            {
693                ItemPosCountVec dest;
694                LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
695                uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
696                if ( msg == EQUIP_ERR_OK )
697                {
698                    item->is_looted = true;
699                    roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
700                    --roll->getLoot()->unlootedCount;
701                    player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId);
702                }
703                else
704                {
705                    item->is_blocked = false;
706                    player->SendEquipError( msg, NULL, NULL );
707                }
708            }
709        }
710    }
711    else if (roll->totalGreed > 0)
712    {
713        if(!roll->playerVote.empty())
714        {
715            uint8 maxresul = 0;
716            uint64 maxguid = (*roll->playerVote.begin()).first;
717            Player *player;
718
719            Roll::PlayerVote::iterator itr;
720            for (itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr)
721            {
722                if (itr->second != GREED)
723                    continue;
724
725                uint8 randomN = urand(1, 99);
726                SendLootRoll(0, itr->first, randomN, 2, *roll);
727                if (maxresul < randomN)
728                {
729                    maxguid  = itr->first;
730                    maxresul = randomN;
731                }
732            }
733            SendLootRollWon(0, maxguid, maxresul, 2, *roll);
734            player = objmgr.GetPlayer(maxguid);
735
736            if(player && player->GetSession())
737            {
738                ItemPosCountVec dest;
739                LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
740                uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
741                if ( msg == EQUIP_ERR_OK )
742                {
743                    item->is_looted = true;
744                    roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
745                    --roll->getLoot()->unlootedCount;
746                    player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId);
747                }
748                else
749                {
750                    item->is_blocked = false;
751                    player->SendEquipError( msg, NULL, NULL );
752                }
753            }
754        }
755    }
756    else
757    {
758        SendLootAllPassed(NumberOfPlayers, *roll);
759        LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
760        if(item) item->is_blocked = false;
761    }
762    RollId.erase(rollI);
763    delete roll;
764}
765
766void Group::SetTargetIcon(uint8 id, uint64 guid)
767{
768    if(id >= TARGETICONCOUNT)
769        return;
770
771    // clean other icons
772    if( guid != 0 )
773        for(int i=0; i<TARGETICONCOUNT; i++)
774            if( m_targetIcons[i] == guid )
775                SetTargetIcon(i, 0);
776
777    m_targetIcons[id] = guid;
778
779    WorldPacket data(MSG_RAID_TARGET_UPDATE, (2+8));
780    data << (uint8)0;
781    data << id;
782    data << guid;
783    BroadcastPacket(&data);
784}
785
786void Group::GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_level, Player* & member_with_max_level)
787{
788    for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
789    {
790        Player* member = itr->getSource();
791        if(!member || !member->isAlive())                   // only for alive
792            continue;
793
794        if(!member->IsAtGroupRewardDistance(victim))        // at req. distance
795            continue;
796
797        ++count;
798        sum_level += member->getLevel();
799        if(!member_with_max_level || member_with_max_level->getLevel() < member->getLevel())
800            member_with_max_level = member;
801    }
802}
803
804void Group::SendTargetIconList(WorldSession *session)
805{
806    if(!session)
807        return;
808
809    WorldPacket data(MSG_RAID_TARGET_UPDATE, (1+TARGETICONCOUNT*9));
810    data << (uint8)1;
811
812    for(int i=0; i<TARGETICONCOUNT; i++)
813    {
814        if(m_targetIcons[i] == 0)
815            continue;
816
817        data << (uint8)i;
818        data << m_targetIcons[i];
819    }
820
821    session->SendPacket(&data);
822}
823
824void Group::SendUpdate()
825{
826    Player *player;
827
828    for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
829    {
830        player = objmgr.GetPlayer(citr->guid);
831        if(!player || !player->GetSession())
832            continue;
833                                                            // guess size
834        WorldPacket data(SMSG_GROUP_LIST, (1+1+1+1+8+4+GetMembersCount()*20));
835        data << (uint8)m_groupType;                         // group type
836        data << (uint8)(isBGGroup() ? 1 : 0);               // 2.0.x, isBattleGroundGroup?
837        data << (uint8)(citr->group);                       // groupid
838        data << (uint8)(citr->assistant?0x01:0);            // 0x2 main assist, 0x4 main tank
839        data << uint64(0x50000000FFFFFFFELL);               // related to voice chat?
840        data << uint32(GetMembersCount()-1);
841        for(member_citerator citr2 = m_memberSlots.begin(); citr2 != m_memberSlots.end(); ++citr2)
842        {
843            if(citr->guid == citr2->guid)
844                continue;
845
846            data << citr2->name;
847            data << (uint64)citr2->guid;
848                                                            // online-state
849            data << (uint8)(objmgr.GetPlayer(citr2->guid) ? 1 : 0);
850            data << (uint8)(citr2->group);                  // groupid
851            data << (uint8)(citr2->assistant?0x01:0);       // 0x2 main assist, 0x4 main tank
852        }
853
854        data << uint64(m_leaderGuid);                       // leader guid
855        if(GetMembersCount()-1)
856        {
857            data << (uint8)m_lootMethod;                    // loot method
858            data << (uint64)m_looterGuid;                   // looter guid
859            data << (uint8)m_lootThreshold;                 // loot threshold
860            data << (uint8)m_difficulty;                    // Heroic Mod Group
861
862        }
863        player->GetSession()->SendPacket( &data );
864    }
865}
866
867void Group::UpdatePlayerOutOfRange(Player* pPlayer)
868{
869    if(!pPlayer)
870        return;
871
872    Player *player;
873    WorldPacket data;
874    pPlayer->GetSession()->BuildPartyMemberStatsChangedPacket(pPlayer, &data);
875
876    for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
877    {
878        player = itr->getSource();
879        if (player && player != pPlayer && !pPlayer->isVisibleFor(player))
880            player->GetSession()->SendPacket(&data);
881    }
882}
883
884void Group::BroadcastPacket(WorldPacket *packet, int group, uint64 ignore)
885{
886    for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
887    {
888        Player *pl = itr->getSource();
889        if(!pl || (ignore != 0 && pl->GetGUID() == ignore))
890            continue;
891
892        if (pl->GetSession() && (group==-1 || itr->getSubGroup()==group))
893            pl->GetSession()->SendPacket(packet);
894    }
895}
896
897void Group::BroadcastReadyCheck(WorldPacket *packet)
898{
899    for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
900    {
901        Player *pl = itr->getSource();
902        if(pl && pl->GetSession())
903            if(IsLeader(pl->GetGUID()) || IsAssistant(pl->GetGUID()))
904                pl->GetSession()->SendPacket(packet);
905    }
906}
907
908void Group::OfflineReadyCheck()
909{
910    for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
911    {
912        Player *pl = objmgr.GetPlayer(citr->guid);
913        if (!pl || !pl->GetSession())
914        {
915            WorldPacket data(MSG_RAID_READY_CHECK_CONFIRM, 9);
916            data << citr->guid;
917            data << (uint8)0;
918            BroadcastReadyCheck(&data);
919        }
920    }
921}
922
923bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant)
924{
925    // get first not-full group
926    uint8 groupid = 0;
927    std::vector<uint8> temp(MAXRAIDSIZE/MAXGROUPSIZE);
928    for(member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr)
929    {
930        if (itr->group >= temp.size()) continue;
931        ++temp[itr->group];
932        if(temp[groupid] >= MAXGROUPSIZE)
933            ++groupid;
934    }
935
936    return _addMember(guid, name, isAssistant, groupid);
937}
938
939bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant, uint8 group)
940{
941    if(IsFull())
942        return false;
943
944    if(!guid)
945        return false;
946
947    Player *player = objmgr.GetPlayer(guid);
948
949    MemberSlot member;
950    member.guid      = guid;
951    member.name      = name;
952    member.group     = group;
953    member.assistant = isAssistant;
954    m_memberSlots.push_back(member);
955
956    if(player)
957    {
958        player->SetGroupInvite(NULL);
959        player->SetGroup(this, group);
960        // if the same group invites the player back, cancel the homebind timer
961        InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player->GetDifficulty());
962        if(bind && bind->save->GetInstanceId() == player->GetInstanceId())
963            player->m_InstanceValid = true;
964    }
965
966    if(!isRaidGroup())                                      // reset targetIcons for non-raid-groups
967    {
968        for(int i=0; i<TARGETICONCOUNT; i++)
969            m_targetIcons[i] = 0;
970    }
971
972    if(!isBGGroup())
973    {
974        // insert into group table
975        CharacterDatabase.PExecute("INSERT INTO group_member(leaderGuid,memberGuid,assistant,subgroup) VALUES('%u','%u','%u','%u')", GUID_LOPART(m_leaderGuid), GUID_LOPART(member.guid), ((member.assistant==1)?1:0), member.group);
976    }
977
978    return true;
979}
980
981bool Group::_removeMember(const uint64 &guid)
982{
983    Player *player = objmgr.GetPlayer(guid);
984    if (player)
985    {
986        player->SetGroup(NULL);
987    }
988
989    _removeRolls(guid);
990
991    member_witerator slot = _getMemberWSlot(guid);
992    if (slot != m_memberSlots.end())
993        m_memberSlots.erase(slot);
994
995    if(!isBGGroup())
996        CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid='%u'", GUID_LOPART(guid));
997
998    if(m_leaderGuid == guid)                                // leader was removed
999    {
1000        if(GetMembersCount() > 0)
1001            _setLeader(m_memberSlots.front().guid);
1002        return true;
1003    }
1004
1005    return false;
1006}
1007
1008void Group::_setLeader(const uint64 &guid)
1009{
1010    member_citerator slot = _getMemberCSlot(guid);
1011    if(slot==m_memberSlots.end())
1012        return;
1013
1014    if(!isBGGroup())
1015    {
1016        // TODO: set a time limit to have this function run rarely cause it can be slow
1017        CharacterDatabase.BeginTransaction();
1018
1019        // update the group's bound instances when changing leaders
1020
1021        // remove all permanent binds from the group
1022        // in the DB also remove solo binds that will be replaced with permbinds
1023        // from the new leader
1024        CharacterDatabase.PExecute(
1025            "DELETE FROM group_instance WHERE leaderguid='%u' AND (permanent = 1 OR "
1026            "instance IN (SELECT instance FROM character_instance WHERE guid = '%u')"
1027            ")", GUID_LOPART(m_leaderGuid), GUID_LOPART(slot->guid)
1028        );
1029
1030        Player *player = objmgr.GetPlayer(slot->guid);
1031        if(player)
1032        {
1033            for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
1034            {
1035                for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end();)
1036                {
1037                    if(itr->second.perm)
1038                    {
1039                        itr->second.save->RemoveGroup(this);
1040                        m_boundInstances[i].erase(itr++);
1041                    }
1042                    else
1043                        ++itr;
1044                }
1045            }
1046        }
1047
1048        // update the group's solo binds to the new leader
1049        CharacterDatabase.PExecute("UPDATE group_instance SET leaderGuid='%u' WHERE leaderGuid = '%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid));
1050
1051        // copy the permanent binds from the new leader to the group
1052        // overwriting the solo binds with permanent ones if necessary
1053        // in the DB those have been deleted already
1054        Player::ConvertInstancesToGroup(player, this, slot->guid);
1055
1056        // update the group leader
1057        CharacterDatabase.PExecute("UPDATE groups SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid));
1058        CharacterDatabase.PExecute("UPDATE group_member SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid));
1059        CharacterDatabase.CommitTransaction();
1060    }
1061
1062    m_leaderGuid = slot->guid;
1063    m_leaderName = slot->name;
1064}
1065
1066void Group::_removeRolls(const uint64 &guid)
1067{
1068    for (Rolls::iterator it = RollId.begin(); it < RollId.end(); it++)
1069    {
1070        Roll* roll = *it;
1071        Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid);
1072        if(itr2 == roll->playerVote.end())
1073            continue;
1074
1075        if (itr2->second == GREED) --roll->totalGreed;
1076        if (itr2->second == NEED) --roll->totalNeed;
1077        if (itr2->second == PASS) --roll->totalPass;
1078        if (itr2->second != NOT_VALID) --roll->totalPlayersRolling;
1079
1080        roll->playerVote.erase(itr2);
1081
1082        CountRollVote(guid, roll->itemGUID, GetMembersCount()-1, 3);
1083    }
1084}
1085
1086void Group::_convertToRaid()
1087{
1088    m_groupType = GROUPTYPE_RAID;
1089
1090    if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
1091}
1092
1093bool Group::_setMembersGroup(const uint64 &guid, const uint8 &group)
1094{
1095    member_witerator slot = _getMemberWSlot(guid);
1096    if(slot==m_memberSlots.end())
1097        return false;
1098
1099    slot->group = group;
1100    if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE group_member SET subgroup='%u' WHERE memberGuid='%u'", group, GUID_LOPART(guid));
1101    return true;
1102}
1103
1104bool Group::_setAssistantFlag(const uint64 &guid, const bool &state)
1105{
1106    member_witerator slot = _getMemberWSlot(guid);
1107    if(slot==m_memberSlots.end())
1108        return false;
1109
1110    slot->assistant = state;
1111    if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE group_member SET assistant='%u' WHERE memberGuid='%u'", (state==true)?1:0, GUID_LOPART(guid));
1112    return true;
1113}
1114
1115bool Group::_setMainTank(const uint64 &guid)
1116{
1117    member_citerator slot = _getMemberCSlot(guid);
1118    if(slot==m_memberSlots.end())
1119        return false;
1120
1121    if(m_mainAssistant == guid)
1122        _setMainAssistant(0);
1123    m_mainTank = guid;
1124    if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET mainTank='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainTank), GUID_LOPART(m_leaderGuid));
1125    return true;
1126}
1127
1128bool Group::_setMainAssistant(const uint64 &guid)
1129{
1130    member_witerator slot = _getMemberWSlot(guid);
1131    if(slot==m_memberSlots.end())
1132        return false;
1133
1134    if(m_mainTank == guid)
1135        _setMainTank(0);
1136    m_mainAssistant = guid;
1137    if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET mainAssistant='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainAssistant), GUID_LOPART(m_leaderGuid));
1138    return true;
1139}
1140
1141bool Group::SameSubGroup(Player const* member1, Player const* member2) const
1142{
1143    if(!member1 || !member2) return false;
1144    if (member1->GetGroup() != this || member2->GetGroup() != this) return false;
1145    else return member1->GetSubGroup() == member2->GetSubGroup();
1146}
1147
1148// allows setting subgroup for offline members
1149void Group::ChangeMembersGroup(const uint64 &guid, const uint8 &group)
1150{
1151    if(!isRaidGroup())
1152        return;
1153    Player *player = objmgr.GetPlayer(guid);
1154    if (!player)
1155    {
1156        if(_setMembersGroup(guid, group))
1157            SendUpdate();
1158    }
1159    else ChangeMembersGroup(player, group);
1160}
1161
1162// only for online members
1163void Group::ChangeMembersGroup(Player *player, const uint8 &group)
1164{
1165    if(!player || !isRaidGroup())
1166        return;
1167    if(_setMembersGroup(player->GetGUID(), group))
1168    {
1169        player->GetGroupRef().setSubGroup(group);
1170        SendUpdate();
1171    }
1172}
1173
1174void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
1175{
1176    switch (GetLootMethod())
1177    {
1178        case MASTER_LOOT:
1179        case FREE_FOR_ALL:
1180            return;
1181        default:
1182            // round robin style looting applies for all low
1183            // quality items in each loot method except free for all and master loot
1184            break;
1185    }
1186
1187    member_citerator guid_itr = _getMemberCSlot(GetLooterGuid());
1188    if(guid_itr != m_memberSlots.end())
1189    {
1190        if(ifneed)
1191        {
1192            // not update if only update if need and ok
1193            Player* looter = ObjectAccessor::FindPlayer(guid_itr->guid);
1194            if(looter && looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
1195                return;
1196        }
1197        ++guid_itr;
1198    }
1199
1200    // search next after current
1201    if(guid_itr != m_memberSlots.end())
1202    {
1203        for(member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr)
1204        {
1205            if(Player* pl = ObjectAccessor::FindPlayer(itr->guid))
1206            {
1207                if (pl->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
1208                {
1209                    bool refresh = pl->GetLootGUID()==creature->GetGUID();
1210
1211                    //if(refresh)                             // update loot for new looter
1212                    //    pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1213                    SetLooterGuid(pl->GetGUID());
1214                    SendUpdate();
1215                    if(refresh)                             // update loot for new looter
1216                        pl->SendLoot(creature->GetGUID(),LOOT_CORPSE);
1217                    return;
1218                }
1219            }
1220        }
1221    }
1222
1223    // search from start
1224    for(member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr)
1225    {
1226        if(Player* pl = ObjectAccessor::FindPlayer(itr->guid))
1227        {
1228            if (pl->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
1229            {
1230                bool refresh = pl->GetLootGUID()==creature->GetGUID();
1231
1232                //if(refresh)                               // update loot for new looter
1233                //    pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1234                SetLooterGuid(pl->GetGUID());
1235                SendUpdate();
1236                if(refresh)                                 // update loot for new looter
1237                    pl->SendLoot(creature->GetGUID(),LOOT_CORPSE);
1238                return;
1239            }
1240        }
1241    }
1242
1243    SetLooterGuid(0);
1244    SendUpdate();
1245}
1246
1247uint32 Group::CanJoinBattleGroundQueue(uint32 bgTypeId, uint32 bgQueueType, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot)
1248{
1249    // check for min / max count
1250    uint32 memberscount = GetMembersCount();
1251    if(memberscount < MinPlayerCount)
1252        return BG_JOIN_ERR_GROUP_NOT_ENOUGH;
1253    if(memberscount > MaxPlayerCount)
1254        return BG_JOIN_ERR_GROUP_TOO_MANY;
1255
1256    // get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.)
1257    Player * reference = GetFirstMember()->getSource();
1258    // no reference found, can't join this way
1259    if(!reference)
1260        return BG_JOIN_ERR_OFFLINE_MEMBER;
1261
1262    uint32 bgQueueId = reference->GetBattleGroundQueueIdFromLevel();
1263    uint32 arenaTeamId = reference->GetArenaTeamId(arenaSlot);
1264    uint32 team = reference->GetTeam();
1265
1266    // check every member of the group to be able to join
1267    for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
1268    {
1269        Player *member = itr->getSource();
1270        // offline member? don't let join
1271        if(!member)
1272            return BG_JOIN_ERR_OFFLINE_MEMBER;
1273        // don't allow cross-faction join as group
1274        if(member->GetTeam() != team)
1275            return BG_JOIN_ERR_MIXED_FACTION;
1276        // not in the same battleground level braket, don't let join
1277        if(member->GetBattleGroundQueueIdFromLevel() != bgQueueId)
1278            return BG_JOIN_ERR_MIXED_LEVELS;
1279        // don't let join rated matches if the arena team id doesn't match
1280        if(isRated && member->GetArenaTeamId(arenaSlot) != arenaTeamId)
1281            return BG_JOIN_ERR_MIXED_ARENATEAM;
1282        // don't let join if someone from the group is already in that bg queue
1283        if(member->InBattleGroundQueueForBattleGroundQueueType(bgQueueType))
1284            return BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE;
1285        // check for deserter debuff in case not arena queue
1286        if(bgTypeId != BATTLEGROUND_AA && !member->CanJoinToBattleground())
1287            return BG_JOIN_ERR_GROUP_DESERTER;
1288        // check if member can join any more battleground queues
1289        if(!member->HasFreeBattleGroundQueueId())
1290            return BG_JOIN_ERR_ALL_QUEUES_USED;
1291    }
1292    return BG_JOIN_ERR_OK;
1293}
1294
1295//===================================================
1296//============== Roll ===============================
1297//===================================================
1298
1299void Roll::targetObjectBuildLink()
1300{
1301    // called from link()
1302    this->getTarget()->addLootValidatorRef(this);
1303}
1304
1305void Group::SetDifficulty(uint8 difficulty)
1306{
1307    m_difficulty = difficulty;
1308    if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET difficulty = %u WHERE leaderGuid ='%u'", m_difficulty, GUID_LOPART(m_leaderGuid));
1309
1310    for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
1311    {
1312        Player *player = itr->getSource();
1313        if(!player->GetSession() || player->getLevel() < LEVELREQUIREMENT_HEROIC)
1314            continue;
1315        player->SetDifficulty(difficulty);
1316        player->SendDungeonDifficulty(true);
1317    }
1318}
1319
1320bool Group::InCombatToInstance(uint32 instanceId)
1321{
1322    for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
1323    {
1324        Player *pPlayer = itr->getSource();
1325        if(pPlayer->getAttackers().size() && pPlayer->GetInstanceId() == instanceId)
1326            return true;
1327    }
1328    return false;
1329}
1330
1331void Group::ResetInstances(uint8 method, Player* SendMsgTo)
1332{
1333    if(isBGGroup())
1334        return;
1335
1336    // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_DISBAND
1337
1338    // we assume that when the difficulty changes, all instances that can be reset will be
1339    uint8 dif = GetDifficulty();
1340
1341    for(BoundInstancesMap::iterator itr = m_boundInstances[dif].begin(); itr != m_boundInstances[dif].end();)
1342    {
1343        InstanceSave *p = itr->second.save;
1344        const MapEntry *entry = sMapStore.LookupEntry(itr->first);
1345        if(!entry || (!p->CanReset() && method != INSTANCE_RESET_GROUP_DISBAND))
1346        {
1347            ++itr;
1348            continue;
1349        }
1350
1351        if(method == INSTANCE_RESET_ALL)
1352        {
1353            // the "reset all instances" method can only reset normal maps
1354            if(dif == DIFFICULTY_HEROIC || entry->map_type == MAP_RAID)
1355            {
1356                ++itr;
1357                continue;
1358            }
1359        }
1360
1361        bool isEmpty = true;
1362        // if the map is loaded, reset it
1363        Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId());
1364        if(map && map->IsDungeon())
1365            isEmpty = ((InstanceMap*)map)->Reset(method);
1366
1367        if(SendMsgTo)
1368        {
1369            if(isEmpty) SendMsgTo->SendResetInstanceSuccess(p->GetMapId());
1370            else SendMsgTo->SendResetInstanceFailed(0, p->GetMapId());
1371        }
1372
1373        if(isEmpty || method == INSTANCE_RESET_GROUP_DISBAND || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
1374        {
1375            // do not reset the instance, just unbind if others are permanently bound to it
1376            if(p->CanReset()) p->DeleteFromDB();
1377            else CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p->GetInstanceId());
1378            // i don't know for sure if hash_map iterators
1379            m_boundInstances[dif].erase(itr);
1380            itr = m_boundInstances[dif].begin();
1381            // this unloads the instance save unless online players are bound to it
1382            // (eg. permanent binds or GM solo binds)
1383            p->RemoveGroup(this);
1384        }
1385        else
1386            ++itr;
1387    }
1388}
1389
1390InstanceGroupBind* Group::GetBoundInstance(uint32 mapid, uint8 difficulty)
1391{
1392    // some instances only have one difficulty
1393    const MapEntry* entry = sMapStore.LookupEntry(mapid);
1394    if(!entry || !entry->SupportsHeroicMode()) difficulty = DIFFICULTY_NORMAL;
1395
1396    BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
1397    if(itr != m_boundInstances[difficulty].end())
1398        return &itr->second;
1399    else
1400        return NULL;
1401}
1402
1403InstanceGroupBind* Group::BindToInstance(InstanceSave *save, bool permanent, bool load)
1404{
1405    if(save && !isBGGroup())
1406    {
1407        InstanceGroupBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
1408        if(bind.save)
1409        {
1410            // when a boss is killed or when copying the players's binds to the group
1411            if(permanent != bind.perm || save != bind.save)
1412                if(!load) CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u', permanent = '%u' WHERE leaderGuid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GUID_LOPART(GetLeaderGUID()), bind.save->GetInstanceId());
1413        }
1414        else
1415            if(!load) CharacterDatabase.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", GUID_LOPART(GetLeaderGUID()), save->GetInstanceId(), permanent);
1416
1417        if(bind.save != save)
1418        {
1419            if(bind.save) bind.save->RemoveGroup(this);
1420            save->AddGroup(this);
1421        }
1422
1423        bind.save = save;
1424        bind.perm = permanent;
1425        if(!load) sLog.outDebug("Group::BindToInstance: %d is now bound to map %d, instance %d, difficulty %d", GUID_LOPART(GetLeaderGUID()), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
1426        return &bind;
1427    }
1428    else
1429        return NULL;
1430}
1431
1432void Group::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
1433{
1434    BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
1435    if(itr != m_boundInstances[difficulty].end())
1436    {
1437        if(!unload) CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", GUID_LOPART(GetLeaderGUID()), itr->second.save->GetInstanceId());
1438        itr->second.save->RemoveGroup(this);                // save can become invalid
1439        m_boundInstances[difficulty].erase(itr);
1440    }
1441}
1442
1443void Group::_homebindIfInstance(Player *player)
1444{
1445    if(player && !player->isGameMaster() && sMapStore.LookupEntry(player->GetMapId())->IsDungeon())
1446    {
1447        // leaving the group in an instance, the homebind timer is started
1448        // unless the player is permanently saved to the instance
1449        InstanceSave *save = sInstanceSaveManager.GetInstanceSave(player->GetInstanceId());
1450        InstancePlayerBind *playerBind = save ? player->GetBoundInstance(save->GetMapId(), save->GetDifficulty()) : NULL;
1451        if(!playerBind || !playerBind->perm)
1452            player->m_InstanceValid = false;
1453    }
1454}
Note: See TracBrowser for help on using the browser.