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

Revision 229, 50.7 kB (checked in by yumileroy, 17 years ago)

[svn] *** Source: MaNGOS ***
* Fixed build extractor at Windows Vista. Author: Vladimir
* Fixed comment text and code indentifiers spelling. Author: Vladimir & Paradox.
* Access cached member lists in guild handlers instead of querying the DB. Author: Hunuza
* Small fixes in send/received packet and simple code cleanup also. Author: Vladimir
* Not output error at loading empty character_ticket table. Author: Vladimir
* Not reset display model at shapeshift aura remove if it not set at apply. Author: Arthorius
* Applied props to few files.

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