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

Revision 111, 50.0 kB (checked in by yumileroy, 17 years ago)

[svn] * Fixed xp calculation for low level characters when grouped with a higher level player. Patch provided by Reiner030.
* Fixed positive spells being not resistable when cast on hostile units. Patch provided by QAston.
* Fixed compile warnings in gcc. Patch provided by WarHead?.

Original author: w12x
Date: 2008-10-26 11:50:07-05:00

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