2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "WorldPacket.h"
22 #include "WorldSession.h"
25 #include "ObjectMgr.h"
26 #include "ObjectGuid.h"
29 #include "ObjectAccessor.h"
30 #include "BattleGround.h"
31 #include "MapManager.h"
32 #include "InstanceSaveMgr.h"
33 #include "MapInstanced.h"
36 Group::Group() : m_Id(0), m_leaderGuid(0), m_mainTank(0), m_mainAssistant(0), m_groupType(GROUPTYPE_NORMAL
),
37 m_dungeonDifficulty(REGULAR_DIFFICULTY
), m_raidDifficulty(REGULAR_DIFFICULTY
),
38 m_bgGroup(NULL
), m_lootMethod(FREE_FOR_ALL
), m_looterGuid(0), m_lootThreshold(ITEM_QUALITY_UNCOMMON
),
39 m_subGroupsCounts(NULL
)
41 for (int i
= 0; i
< TARGETICONCOUNT
; ++i
)
49 sLog
.outDebug("Group::~Group: battleground group being deleted.");
50 if(m_bgGroup
->GetBgRaid(ALLIANCE
) == this)
51 m_bgGroup
->SetBgRaid(ALLIANCE
, NULL
);
52 else if(m_bgGroup
->GetBgRaid(HORDE
) == this)
53 m_bgGroup
->SetBgRaid(HORDE
, NULL
);
55 sLog
.outError("Group::~Group: battleground group is not linked to the correct battleground.");
58 while(!RollId
.empty())
66 // it is undefined whether objectmgr (which stores the groups) or instancesavemgr
67 // will be unloaded first so we must be prepared for both cases
68 // this may unload some instance saves
69 for(uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
70 for(BoundInstancesMap::iterator itr2
= m_boundInstances
[i
].begin(); itr2
!= m_boundInstances
[i
].end(); ++itr2
)
71 itr2
->second
.save
->RemoveGroup(this);
73 // Sub group counters clean up
74 if (m_subGroupsCounts
)
75 delete[] m_subGroupsCounts
;
78 bool Group::Create(const uint64
&guid
, const char * name
)
83 m_groupType
= isBGGroup() ? GROUPTYPE_RAID
: GROUPTYPE_NORMAL
;
85 if (m_groupType
== GROUPTYPE_RAID
)
86 _initRaidSubGroupsCounter();
88 m_lootMethod
= GROUP_LOOT
;
89 m_lootThreshold
= ITEM_QUALITY_UNCOMMON
;
92 m_dungeonDifficulty
= DUNGEON_DIFFICULTY_NORMAL
;
93 m_raidDifficulty
= RAID_DIFFICULTY_10MAN_NORMAL
;
96 m_Id
= sObjectMgr
.GenerateGroupId();
98 Player
*leader
= sObjectMgr
.GetPlayer(guid
);
101 m_dungeonDifficulty
= leader
->GetDungeonDifficulty();
102 m_raidDifficulty
= leader
->GetRaidDifficulty();
105 Player::ConvertInstancesToGroup(leader
, this, guid
);
107 // store group in database
108 CharacterDatabase
.BeginTransaction();
109 CharacterDatabase
.PExecute("DELETE FROM groups WHERE groupId ='%u'", m_Id
);
110 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE groupId ='%u'", m_Id
);
111 CharacterDatabase
.PExecute("INSERT INTO groups (groupId,leaderGuid,mainTank,mainAssistant,lootMethod,looterGuid,lootThreshold,icon1,icon2,icon3,icon4,icon5,icon6,icon7,icon8,isRaid,difficulty,raiddifficulty) "
112 "VALUES ('%u','%u','%u','%u','%u','%u','%u','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','%u','%u','%u')",
113 m_Id
, GUID_LOPART(m_leaderGuid
), GUID_LOPART(m_mainTank
), GUID_LOPART(m_mainAssistant
), uint32(m_lootMethod
),
114 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(), uint32(m_dungeonDifficulty
), m_raidDifficulty
);
117 if(!AddMember(guid
, name
))
121 CharacterDatabase
.CommitTransaction();
126 bool Group::LoadGroupFromDB(Field
* fields
)
128 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
129 // result = CharacterDatabase.Query("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty, raiddifficulty, leaderGuid, groupId FROM groups");
131 m_Id
= fields
[17].GetUInt32();
132 m_leaderGuid
= MAKE_NEW_GUID(fields
[16].GetUInt32(),0,HIGHGUID_PLAYER
);
134 // group leader not exist
135 if(!sObjectMgr
.GetPlayerNameByGUID(m_leaderGuid
, m_leaderName
))
138 m_groupType
= fields
[13].GetBool() ? GROUPTYPE_RAID
: GROUPTYPE_NORMAL
;
140 if (m_groupType
== GROUPTYPE_RAID
)
141 _initRaidSubGroupsCounter();
143 uint32 diff
= fields
[14].GetUInt8();
144 if (diff
>= MAX_DUNGEON_DIFFICULTY
)
145 diff
= DUNGEON_DIFFICULTY_NORMAL
;
146 m_dungeonDifficulty
= Difficulty(diff
);
148 uint32 r_diff
= fields
[15].GetUInt8();
149 if (r_diff
>= MAX_RAID_DIFFICULTY
)
150 r_diff
= RAID_DIFFICULTY_10MAN_NORMAL
;
151 m_raidDifficulty
= Difficulty(r_diff
);
153 m_mainTank
= fields
[0].GetUInt64();
154 m_mainAssistant
= fields
[1].GetUInt64();
155 m_lootMethod
= (LootMethod
)fields
[2].GetUInt8();
156 m_looterGuid
= MAKE_NEW_GUID(fields
[3].GetUInt32(), 0, HIGHGUID_PLAYER
);
157 m_lootThreshold
= (ItemQualities
)fields
[4].GetUInt16();
159 for(int i
= 0; i
< TARGETICONCOUNT
; ++i
)
160 m_targetIcons
[i
] = fields
[5+i
].GetUInt64();
165 bool Group::LoadMemberFromDB(uint32 guidLow
, uint8 subgroup
, bool assistant
)
168 member
.guid
= MAKE_NEW_GUID(guidLow
, 0, HIGHGUID_PLAYER
);
170 // skip non-existed member
171 if(!sObjectMgr
.GetPlayerNameByGUID(member
.guid
, member
.name
))
174 member
.group
= subgroup
;
175 member
.assistant
= assistant
;
176 m_memberSlots
.push_back(member
);
178 SubGroupCounterIncrease(subgroup
);
183 void Group::ConvertToRaid()
185 m_groupType
= GROUPTYPE_RAID
;
187 _initRaidSubGroupsCounter();
190 CharacterDatabase
.PExecute("UPDATE groups SET isRaid = 1 WHERE groupId='%u'", m_Id
);
193 // update quest related GO states (quest activity dependent from raid membership)
194 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
195 if(Player
* player
= sObjectMgr
.GetPlayer(citr
->guid
))
196 player
->UpdateForQuestWorldObjects();
199 bool Group::AddInvite(Player
*player
)
201 if( !player
|| player
->GetGroupInvite() )
203 Group
* group
= player
->GetGroup();
204 if( group
&& group
->isBGGroup() )
205 group
= player
->GetOriginalGroup();
209 RemoveInvite(player
);
211 m_invitees
.insert(player
);
213 player
->SetGroupInvite(this);
218 bool Group::AddLeaderInvite(Player
*player
)
220 if(!AddInvite(player
))
223 m_leaderGuid
= player
->GetGUID();
224 m_leaderName
= player
->GetName();
228 uint32
Group::RemoveInvite(Player
*player
)
230 m_invitees
.erase(player
);
232 player
->SetGroupInvite(NULL
);
233 return GetMembersCount();
236 void Group::RemoveAllInvites()
238 for(InvitesList::iterator itr
=m_invitees
.begin(); itr
!=m_invitees
.end(); ++itr
)
239 (*itr
)->SetGroupInvite(NULL
);
244 Player
* Group::GetInvited(const uint64
& guid
) const
246 for(InvitesList::const_iterator itr
= m_invitees
.begin(); itr
!= m_invitees
.end(); ++itr
)
248 if((*itr
)->GetGUID() == guid
)
254 Player
* Group::GetInvited(const std::string
& name
) const
256 for(InvitesList::const_iterator itr
= m_invitees
.begin(); itr
!= m_invitees
.end(); ++itr
)
258 if((*itr
)->GetName() == name
)
264 bool Group::AddMember(const uint64
&guid
, const char* name
)
266 if(!_addMember(guid
, name
))
270 Player
*player
= sObjectMgr
.GetPlayer(guid
);
273 if(!IsLeader(player
->GetGUID()) && !isBGGroup())
275 // reset the new member's instances, unless he is currently in one of them
276 // including raid/heroic instances that they are not permanently bound to!
277 player
->ResetInstances(INSTANCE_RESET_GROUP_JOIN
,false);
278 player
->ResetInstances(INSTANCE_RESET_GROUP_JOIN
,true);
280 if (player
->getLevel() >= LEVELREQUIREMENT_HEROIC
)
282 if (player
->GetDungeonDifficulty() != GetDungeonDifficulty())
284 player
->SetDungeonDifficulty(GetDungeonDifficulty());
285 player
->SendDungeonDifficulty(true);
287 if (player
->GetRaidDifficulty() != GetRaidDifficulty())
289 player
->SetRaidDifficulty(GetRaidDifficulty());
290 player
->SendRaidDifficulty(true);
294 player
->SetGroupUpdateFlag(GROUP_UPDATE_FULL
);
295 UpdatePlayerOutOfRange(player
);
297 // quest related GO state dependent from raid membership
299 player
->UpdateForQuestWorldObjects();
305 uint32
Group::RemoveMember(const uint64
&guid
, const uint8
&method
)
307 // remove member and change leader (if need) only if strong more 2 members _before_ member remove
308 if(GetMembersCount() > uint32(isBGGroup() ? 1 : 2)) // in BG group case allow 1 members group
310 bool leaderChanged
= _removeMember(guid
);
312 if(Player
*player
= sObjectMgr
.GetPlayer( guid
))
314 // quest related GO state dependent from raid membership
316 player
->UpdateForQuestWorldObjects();
322 data
.Initialize( SMSG_GROUP_UNINVITE
, 0 );
323 player
->GetSession()->SendPacket( &data
);
326 //we already removed player from group and in player->GetGroup() is his original group!
327 if( Group
* group
= player
->GetGroup() )
333 data
.Initialize(SMSG_GROUP_LIST
, 1+1+1+1+8+4+4+8);
334 data
<< uint8(0x10) << uint8(0) << uint8(0) << uint8(0);
335 data
<< uint64(0) << uint32(0) << uint32(0) << uint64(0);
336 player
->GetSession()->SendPacket(&data
);
339 _homebindIfInstance(player
);
344 WorldPacket
data(SMSG_GROUP_SET_LEADER
, (m_memberSlots
.front().name
.size()+1));
345 data
<< m_memberSlots
.front().name
;
346 BroadcastPacket(&data
, true);
351 // if group before remove <= 2 disband it
355 return m_memberSlots
.size();
358 void Group::ChangeLeader(const uint64
&guid
)
360 member_citerator slot
= _getMemberCSlot(guid
);
362 if(slot
== m_memberSlots
.end())
367 WorldPacket
data(SMSG_GROUP_SET_LEADER
, slot
->name
.size()+1);
369 BroadcastPacket(&data
, true);
373 void Group::Disband(bool hideDestroy
)
377 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
379 player
= sObjectMgr
.GetPlayer(citr
->guid
);
383 //we cannot call _removeMember because it would invalidate member iterator
384 //if we are removing player from battleground raid
386 player
->RemoveFromBattleGroundRaid();
389 //we can remove player who is in battleground from his original group
390 if( player
->GetOriginalGroup() == this )
391 player
->SetOriginalGroup(NULL
);
393 player
->SetGroup(NULL
);
396 // quest related GO state dependent from raid membership
398 player
->UpdateForQuestWorldObjects();
400 if(!player
->GetSession())
406 data
.Initialize(SMSG_GROUP_DESTROYED
, 0);
407 player
->GetSession()->SendPacket(&data
);
410 //we already removed player from group and in player->GetGroup() is his original group, send update
411 if( Group
* group
= player
->GetGroup() )
417 data
.Initialize(SMSG_GROUP_LIST
, 1+1+1+1+8+4+4+8);
418 data
<< uint8(0x10) << uint8(0) << uint8(0) << uint8(0);
419 data
<< uint64(0) << uint32(0) << uint32(0) << uint64(0);
420 player
->GetSession()->SendPacket(&data
);
423 _homebindIfInstance(player
);
426 m_memberSlots
.clear();
432 CharacterDatabase
.BeginTransaction();
433 CharacterDatabase
.PExecute("DELETE FROM groups WHERE groupId='%u'", m_Id
);
434 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE groupId='%u'", m_Id
);
435 CharacterDatabase
.CommitTransaction();
436 ResetInstances(INSTANCE_RESET_GROUP_DISBAND
, false, NULL
);
437 ResetInstances(INSTANCE_RESET_GROUP_DISBAND
, true, NULL
);
444 /*********************************************************/
445 /*** LOOT SYSTEM ***/
446 /*********************************************************/
448 void Group::SendLootStartRoll(uint32 CountDown
, const Roll
&r
)
450 WorldPacket
data(SMSG_LOOT_START_ROLL
, (8+4+4+4+4+4+4+1));
451 data
<< uint64(r
.itemGUID
); // guid of rolled item
452 data
<< uint32(r
.totalPlayersRolling
); // maybe the number of players rolling for it???
453 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
454 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
455 data
<< uint32(r
.itemRandomPropId
); // item random property ID
456 data
<< uint32(r
.itemCount
); // items in stack
457 data
<< uint32(CountDown
); // the countdown time to choose "need" or "greed"
458 data
<< uint8(ALL_ROLL_TYPE_MASK
); // roll type mask
460 for (Roll::PlayerVote::const_iterator itr
= r
.playerVote
.begin(); itr
!= r
.playerVote
.end(); ++itr
)
462 Player
*p
= sObjectMgr
.GetPlayer(itr
->first
);
463 if(!p
|| !p
->GetSession())
466 if(itr
->second
!= NOT_VALID
)
467 p
->GetSession()->SendPacket( &data
);
471 void Group::SendLootRoll(const uint64
& SourceGuid
, const uint64
& TargetGuid
, uint8 RollNumber
, uint8 RollType
, const Roll
&r
)
473 WorldPacket
data(SMSG_LOOT_ROLL
, (8+4+8+4+4+4+1+1+1));
474 data
<< uint64(SourceGuid
); // guid of the item rolled
475 data
<< uint32(0); // unknown, maybe amount of players
476 data
<< uint64(TargetGuid
);
477 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
478 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
479 data
<< uint32(r
.itemRandomPropId
); // Item random property ID
480 data
<< uint8(RollNumber
); // 0: "Need for: [item name]" > 127: "you passed on: [item name]" Roll number
481 data
<< uint8(RollType
); // 0: "Need for: [item name]" 0: "You have selected need for [item name] 1: need roll 2: greed roll
482 data
<< uint8(0); // 2.4.0
484 for( Roll::PlayerVote::const_iterator itr
= r
.playerVote
.begin(); itr
!= r
.playerVote
.end(); ++itr
)
486 Player
*p
= sObjectMgr
.GetPlayer(itr
->first
);
487 if(!p
|| !p
->GetSession())
490 if(itr
->second
!= NOT_VALID
)
491 p
->GetSession()->SendPacket( &data
);
495 void Group::SendLootRollWon(const uint64
& SourceGuid
, const uint64
& TargetGuid
, uint8 RollNumber
, uint8 RollType
, const Roll
&r
)
497 WorldPacket
data(SMSG_LOOT_ROLL_WON
, (8+4+4+4+4+8+1+1));
498 data
<< uint64(SourceGuid
); // guid of the item rolled
499 data
<< uint32(0); // unknown, maybe amount of players
500 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
501 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
502 data
<< uint32(r
.itemRandomPropId
); // Item random property
503 data
<< uint64(TargetGuid
); // guid of the player who won.
504 data
<< uint8(RollNumber
); // rollnumber realted to SMSG_LOOT_ROLL
505 data
<< uint8(RollType
); // Rolltype related to SMSG_LOOT_ROLL
507 for( Roll::PlayerVote::const_iterator itr
= r
.playerVote
.begin(); itr
!= r
.playerVote
.end(); ++itr
)
509 Player
*p
= sObjectMgr
.GetPlayer(itr
->first
);
510 if(!p
|| !p
->GetSession())
513 if(itr
->second
!= NOT_VALID
)
514 p
->GetSession()->SendPacket( &data
);
518 void Group::SendLootAllPassed(uint32 NumberOfPlayers
, const Roll
&r
)
520 WorldPacket
data(SMSG_LOOT_ALL_PASSED
, (8+4+4+4+4));
521 data
<< uint64(r
.itemGUID
); // Guid of the item rolled
522 data
<< uint32(NumberOfPlayers
); // The number of players rolling for it???
523 data
<< uint32(r
.itemid
); // The itemEntryId for the item that shall be rolled for
524 data
<< uint32(r
.itemRandomPropId
); // Item random property ID
525 data
<< uint32(r
.itemRandomSuffix
); // Item random suffix ID
527 for( Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
529 Player
*p
= sObjectMgr
.GetPlayer(itr
->first
);
530 if(!p
|| !p
->GetSession())
533 if(itr
->second
!= NOT_VALID
)
534 p
->GetSession()->SendPacket( &data
);
538 void Group::GroupLoot(const uint64
& playerGUID
, Loot
*loot
, Creature
*creature
)
540 std::vector
<LootItem
>::iterator i
;
541 ItemPrototype
const *item
;
543 Player
*player
= sObjectMgr
.GetPlayer(playerGUID
);
544 Group
*group
= player
->GetGroup();
546 for (i
= loot
->items
.begin(); i
!= loot
->items
.end(); ++i
, ++itemSlot
)
548 item
= ObjectMgr::GetItemPrototype(i
->itemid
);
551 //sLog.outDebug("Group::GroupLoot: missing item prototype for item with id: %d", i->itemid);
555 //roll for over-threshold item if it's one-player loot
556 if (item
->Quality
>= uint32(m_lootThreshold
) && !i
->freeforall
)
558 uint64 newitemGUID
= MAKE_NEW_GUID(sObjectMgr
.GenerateLowGuid(HIGHGUID_ITEM
), 0, HIGHGUID_ITEM
);
559 Roll
* r
= new Roll(newitemGUID
, *i
);
561 //a vector is filled with only near party members
562 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
564 Player
*member
= itr
->getSource();
565 if(!member
|| !member
->GetSession())
567 if ( i
->AllowedForPlayer(member
) )
569 if (member
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
571 r
->playerVote
[member
->GetGUID()] = NOT_EMITED_YET
;
572 ++r
->totalPlayersRolling
;
578 r
->itemSlot
= itemSlot
;
580 group
->SendLootStartRoll(60000, *r
);
582 loot
->items
[itemSlot
].is_blocked
= true;
583 creature
->m_groupLootTimer
= 60000;
584 creature
->m_groupLootId
= GetId();
589 i
->is_underthreshold
= 1;
593 void Group::NeedBeforeGreed(const uint64
& playerGUID
, Loot
*loot
, Creature
*creature
)
595 ItemPrototype
const *item
;
596 Player
*player
= sObjectMgr
.GetPlayer(playerGUID
);
597 Group
*group
= player
->GetGroup();
600 for(std::vector
<LootItem
>::iterator i
=loot
->items
.begin(); i
!= loot
->items
.end(); ++i
, ++itemSlot
)
602 item
= ObjectMgr::GetItemPrototype(i
->itemid
);
604 //only roll for one-player items, not for ones everyone can get
605 if (item
->Quality
>= uint32(m_lootThreshold
) && !i
->freeforall
)
607 uint64 newitemGUID
= MAKE_NEW_GUID(sObjectMgr
.GenerateLowGuid(HIGHGUID_ITEM
), 0, HIGHGUID_ITEM
);
608 Roll
* r
= new Roll(newitemGUID
, *i
);
610 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
612 Player
*playerToRoll
= itr
->getSource();
613 if(!playerToRoll
|| !playerToRoll
->GetSession())
616 if (playerToRoll
->CanUseItem(item
) && i
->AllowedForPlayer(playerToRoll
) )
618 if (playerToRoll
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
620 r
->playerVote
[playerToRoll
->GetGUID()] = NOT_EMITED_YET
;
621 ++r
->totalPlayersRolling
;
626 if (r
->totalPlayersRolling
> 0)
629 r
->itemSlot
= itemSlot
;
631 group
->SendLootStartRoll(60000, *r
);
633 loot
->items
[itemSlot
].is_blocked
= true;
643 i
->is_underthreshold
= 1;
647 void Group::MasterLoot(const uint64
& playerGUID
, Loot
* /*loot*/, Creature
*creature
)
649 Player
*player
= sObjectMgr
.GetPlayer(playerGUID
);
653 sLog
.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330) player = [%s].", player
->GetName());
655 uint32 real_count
= 0;
657 WorldPacket
data(SMSG_LOOT_MASTER_LIST
, 330);
658 data
<< (uint8
)GetMembersCount();
660 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
662 Player
*looter
= itr
->getSource();
663 if (!looter
->IsInWorld())
666 if (looter
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
668 data
<< looter
->GetGUID();
673 data
.put
<uint8
>(0, real_count
);
675 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
677 Player
*looter
= itr
->getSource();
678 if (looter
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
679 looter
->GetSession()->SendPacket(&data
);
683 void Group::CountRollVote(const uint64
& playerGUID
, const uint64
& Guid
, uint32 NumberOfPlayers
, uint8 Choise
)
685 Rolls::iterator rollI
= GetRoll(Guid
);
686 if (rollI
== RollId
.end())
690 Roll::PlayerVote::iterator itr
= roll
->playerVote
.find(playerGUID
);
691 // this condition means that player joins to the party after roll begins
692 if (itr
== roll
->playerVote
.end())
696 if (roll
->getLoot()->items
.empty())
701 case ROLL_PASS
: // Player choose pass
703 SendLootRoll(0, playerGUID
, 0, ROLL_PASS
, *roll
);
708 case ROLL_NEED
: // player choose Need
710 SendLootRoll(0, playerGUID
, 0, ROLL_NEED
, *roll
);
715 case ROLL_GREED
: // player choose Greed
717 SendLootRoll(0, playerGUID
, 128, ROLL_GREED
, *roll
);
722 case ROLL_DISENCHANT
: // player choose Disenchant
724 SendLootRoll(0, playerGUID
, 128, ROLL_DISENCHANT
, *roll
);
726 itr
->second
= DISENCHANT
;
730 if (roll
->totalPass
+ roll
->totalNeed
+ roll
->totalGreed
>= roll
->totalPlayersRolling
)
732 CountTheRoll(rollI
, NumberOfPlayers
);
736 //called when roll timer expires
737 void Group::EndRoll()
740 while(!RollId
.empty())
742 //need more testing here, if rolls disappear
743 itr
= RollId
.begin();
744 CountTheRoll(itr
, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass
748 void Group::CountTheRoll(Rolls::iterator rollI
, uint32 NumberOfPlayers
)
751 if(!roll
->isValid()) // is loot already deleted ?
758 if (roll
->totalNeed
> 0)
760 if(!roll
->playerVote
.empty())
763 uint64 maxguid
= (*roll
->playerVote
.begin()).first
;
766 for( Roll::PlayerVote::const_iterator itr
= roll
->playerVote
.begin(); itr
!= roll
->playerVote
.end(); ++itr
)
768 if (itr
->second
!= NEED
)
771 uint8 randomN
= urand(1, 99);
772 SendLootRoll(0, itr
->first
, randomN
, ROLL_NEED
, *roll
);
773 if (maxresul
< randomN
)
775 maxguid
= itr
->first
;
779 SendLootRollWon(0, maxguid
, maxresul
, ROLL_NEED
, *roll
);
780 player
= sObjectMgr
.GetPlayer(maxguid
);
782 if(player
&& player
->GetSession())
784 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT
, roll
->itemid
, maxresul
);
786 ItemPosCountVec dest
;
787 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
788 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, roll
->itemid
, item
->count
);
789 if ( msg
== EQUIP_ERR_OK
)
791 item
->is_looted
= true;
792 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
793 --roll
->getLoot()->unlootedCount
;
794 player
->StoreNewItem( dest
, roll
->itemid
, true, item
->randomPropertyId
);
798 item
->is_blocked
= false;
799 player
->SendEquipError( msg
, NULL
, NULL
, roll
->itemid
);
804 else if (roll
->totalGreed
> 0)
806 if(!roll
->playerVote
.empty())
809 uint64 maxguid
= (*roll
->playerVote
.begin()).first
;
811 RollVote rollvote
= PASS
; //Fixed: Using uninitialized memory 'rollvote'
813 Roll::PlayerVote::iterator itr
;
814 for (itr
= roll
->playerVote
.begin(); itr
!= roll
->playerVote
.end(); ++itr
)
816 if (itr
->second
!= GREED
&& itr
->second
!= DISENCHANT
)
819 uint8 randomN
= urand(1, 99);
820 SendLootRoll(0, itr
->first
, randomN
, itr
->second
, *roll
);
821 if (maxresul
< randomN
)
823 maxguid
= itr
->first
;
825 rollvote
= itr
->second
;
828 SendLootRollWon(0, maxguid
, maxresul
, rollvote
, *roll
);
829 player
= sObjectMgr
.GetPlayer(maxguid
);
831 if(player
&& player
->GetSession())
833 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT
, roll
->itemid
, maxresul
);
835 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
837 if(rollvote
== GREED
)
839 ItemPosCountVec dest
;
840 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, roll
->itemid
, item
->count
);
841 if ( msg
== EQUIP_ERR_OK
)
843 item
->is_looted
= true;
844 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
845 --roll
->getLoot()->unlootedCount
;
846 player
->StoreNewItem( dest
, roll
->itemid
, true, item
->randomPropertyId
);
850 item
->is_blocked
= false;
851 player
->SendEquipError( msg
, NULL
, NULL
, roll
->itemid
);
854 else if(rollvote
== DISENCHANT
)
856 item
->is_looted
= true;
857 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
858 --roll
->getLoot()->unlootedCount
;
860 ItemPrototype
const *pProto
= ObjectMgr::GetItemPrototype(roll
->itemid
);
861 player
->AutoStoreLoot(pProto
->DisenchantID
, LootTemplates_Disenchant
, true);
868 SendLootAllPassed(NumberOfPlayers
, *roll
);
869 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
870 if(item
) item
->is_blocked
= false;
876 void Group::SetTargetIcon(uint8 id
, uint64 whoGuid
, uint64 targetGuid
)
878 if(id
>= TARGETICONCOUNT
)
882 if( targetGuid
!= 0 )
883 for(int i
= 0; i
< TARGETICONCOUNT
; ++i
)
884 if( m_targetIcons
[i
] == targetGuid
)
885 SetTargetIcon(i
, 0, 0);
887 m_targetIcons
[id
] = targetGuid
;
889 WorldPacket
data(MSG_RAID_TARGET_UPDATE
, (1+8+1+8));
890 data
<< uint8(0); // set targets
891 data
<< uint64(whoGuid
);
893 data
<< uint64(targetGuid
);
894 BroadcastPacket(&data
, true);
897 void Group::GetDataForXPAtKill(Unit
const* victim
, uint32
& count
,uint32
& sum_level
, Player
* & member_with_max_level
, Player
* & not_gray_member_with_max_level
)
899 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
901 Player
* member
= itr
->getSource();
902 if(!member
|| !member
->isAlive()) // only for alive
905 if(!member
->IsAtGroupRewardDistance(victim
)) // at req. distance
909 sum_level
+= member
->getLevel();
910 if(!member_with_max_level
|| member_with_max_level
->getLevel() < member
->getLevel())
911 member_with_max_level
= member
;
913 uint32 gray_level
= MaNGOS::XP::GetGrayLevel(member
->getLevel());
914 if( victim
->getLevel() > gray_level
&& (!not_gray_member_with_max_level
915 || not_gray_member_with_max_level
->getLevel() < member
->getLevel()))
916 not_gray_member_with_max_level
= member
;
920 void Group::SendTargetIconList(WorldSession
*session
)
925 WorldPacket
data(MSG_RAID_TARGET_UPDATE
, (1+TARGETICONCOUNT
*9));
926 data
<< uint8(1); // list targets
928 for(int i
= 0; i
< TARGETICONCOUNT
; ++i
)
930 if(m_targetIcons
[i
] == 0)
934 data
<< uint64(m_targetIcons
[i
]);
937 session
->SendPacket(&data
);
940 void Group::SendUpdate()
944 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
946 player
= sObjectMgr
.GetPlayer(citr
->guid
);
947 if(!player
|| !player
->GetSession() || player
->GetGroup() != this )
950 WorldPacket
data(SMSG_GROUP_LIST
, (1+1+1+1+8+4+GetMembersCount()*20));
951 data
<< uint8(m_groupType
); // group type (flags in 3.3)
952 data
<< uint8(isBGGroup() ? 1 : 0); // 2.0.x, isBattleGroundGroup?
953 data
<< uint8(citr
->group
); // groupid
954 data
<< uint8(citr
->assistant
? 0x01 : 0x00); // 0x2 main assist, 0x4 main tank
955 if(m_groupType
& GROUPTYPE_LFD
)
960 data
<< uint64(0x50000000FFFFFFFELL
); // related to voice chat?
961 data
<< uint32(0); // 3.3, this value increments every time SMSG_GROUP_LIST is sent
962 data
<< uint32(GetMembersCount()-1);
963 for(member_citerator citr2
= m_memberSlots
.begin(); citr2
!= m_memberSlots
.end(); ++citr2
)
965 if(citr
->guid
== citr2
->guid
)
967 Player
* member
= sObjectMgr
.GetPlayer(citr2
->guid
);
968 uint8 onlineState
= (member
) ? MEMBER_STATUS_ONLINE
: MEMBER_STATUS_OFFLINE
;
969 onlineState
= onlineState
| ((isBGGroup()) ? MEMBER_STATUS_PVP
: 0);
972 data
<< uint64(citr2
->guid
);
974 data
<< uint8(onlineState
);
975 data
<< uint8(citr2
->group
); // groupid
976 data
<< uint8(citr2
->assistant
?0x01:0); // 0x2 main assist, 0x4 main tank
977 data
<< uint8(0); // 3.3, role?
980 data
<< uint64(m_leaderGuid
); // leader guid
981 if(GetMembersCount()-1)
983 data
<< uint8(m_lootMethod
); // loot method
984 data
<< uint64(m_looterGuid
); // looter guid
985 data
<< uint8(m_lootThreshold
); // loot threshold
986 data
<< uint8(m_dungeonDifficulty
); // Dungeon Difficulty
987 data
<< uint8(m_raidDifficulty
); // Raid Difficulty
988 data
<< uint8(0); // 3.3, dynamic difficulty?
990 player
->GetSession()->SendPacket( &data
);
994 void Group::UpdatePlayerOutOfRange(Player
* pPlayer
)
996 if(!pPlayer
|| !pPlayer
->IsInWorld())
1001 pPlayer
->GetSession()->BuildPartyMemberStatsChangedPacket(pPlayer
, &data
);
1003 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1005 player
= itr
->getSource();
1006 if (player
&& player
!= pPlayer
&& !pPlayer
->isVisibleFor(player
,player
->GetViewPoint()))
1007 player
->GetSession()->SendPacket(&data
);
1011 void Group::BroadcastPacket(WorldPacket
*packet
, bool ignorePlayersInBGRaid
, int group
, uint64 ignore
)
1013 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1015 Player
*pl
= itr
->getSource();
1016 if(!pl
|| (ignore
!= 0 && pl
->GetGUID() == ignore
) || (ignorePlayersInBGRaid
&& pl
->GetGroup() != this) )
1019 if (pl
->GetSession() && (group
== -1 || itr
->getSubGroup() == group
))
1020 pl
->GetSession()->SendPacket(packet
);
1024 void Group::BroadcastReadyCheck(WorldPacket
*packet
)
1026 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1028 Player
*pl
= itr
->getSource();
1029 if(pl
&& pl
->GetSession())
1030 if(IsLeader(pl
->GetGUID()) || IsAssistant(pl
->GetGUID()))
1031 pl
->GetSession()->SendPacket(packet
);
1035 void Group::OfflineReadyCheck()
1037 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
1039 Player
*pl
= sObjectMgr
.GetPlayer(citr
->guid
);
1040 if (!pl
|| !pl
->GetSession())
1042 WorldPacket
data(MSG_RAID_READY_CHECK_CONFIRM
, 9);
1045 BroadcastReadyCheck(&data
);
1050 bool Group::_addMember(const uint64
&guid
, const char* name
, bool isAssistant
)
1052 // get first not-full group
1054 if (m_subGroupsCounts
)
1056 bool groupFound
= false;
1057 for (; groupid
< MAXRAIDSIZE
/ MAXGROUPSIZE
; ++groupid
)
1059 if (m_subGroupsCounts
[groupid
] < MAXGROUPSIZE
)
1065 // We are raid group and no one slot is free
1070 return _addMember(guid
, name
, isAssistant
, groupid
);
1073 bool Group::_addMember(const uint64
&guid
, const char* name
, bool isAssistant
, uint8 group
)
1081 Player
*player
= sObjectMgr
.GetPlayer(guid
);
1086 member
.group
= group
;
1087 member
.assistant
= isAssistant
;
1088 m_memberSlots
.push_back(member
);
1090 SubGroupCounterIncrease(group
);
1094 player
->SetGroupInvite(NULL
);
1095 //if player is in group and he is being added to BG raid group, then call SetBattleGroundRaid()
1096 if( player
->GetGroup() && isBGGroup() )
1097 player
->SetBattleGroundRaid(this, group
);
1098 //if player is in bg raid and we are adding him to normal group, then call SetOriginalGroup()
1099 else if ( player
->GetGroup() )
1100 player
->SetOriginalGroup(this, group
);
1101 //if player is not in group, then call set group
1103 player
->SetGroup(this, group
);
1104 // if the same group invites the player back, cancel the homebind timer
1105 InstanceGroupBind
*bind
= GetBoundInstance(player
);
1106 if(bind
&& bind
->save
->GetInstanceId() == player
->GetInstanceId())
1107 player
->m_InstanceValid
= true;
1110 if(!isRaidGroup()) // reset targetIcons for non-raid-groups
1112 for(int i
= 0; i
< TARGETICONCOUNT
; ++i
)
1113 m_targetIcons
[i
] = 0;
1118 // insert into group table
1119 CharacterDatabase
.PExecute("INSERT INTO group_member(groupId,memberGuid,assistant,subgroup) VALUES('%u','%u','%u','%u')", m_Id
, GUID_LOPART(member
.guid
), ((member
.assistant
==1)?1:0), member
.group
);
1125 bool Group::_removeMember(const uint64
&guid
)
1127 Player
*player
= sObjectMgr
.GetPlayer(guid
);
1130 //if we are removing player from battleground raid
1132 player
->RemoveFromBattleGroundRaid();
1135 //we can remove player who is in battleground from his original group
1136 if( player
->GetOriginalGroup() == this )
1137 player
->SetOriginalGroup(NULL
);
1139 player
->SetGroup(NULL
);
1145 member_witerator slot
= _getMemberWSlot(guid
);
1146 if (slot
!= m_memberSlots
.end())
1148 SubGroupCounterDecrease(slot
->group
);
1150 m_memberSlots
.erase(slot
);
1154 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE memberGuid='%u'", GUID_LOPART(guid
));
1156 if(m_leaderGuid
== guid
) // leader was removed
1158 if(GetMembersCount() > 0)
1159 _setLeader(m_memberSlots
.front().guid
);
1166 void Group::_setLeader(const uint64
&guid
)
1168 member_citerator slot
= _getMemberCSlot(guid
);
1169 if(slot
==m_memberSlots
.end())
1174 // TODO: set a time limit to have this function run rarely cause it can be slow
1175 CharacterDatabase
.BeginTransaction();
1177 // update the group's bound instances when changing leaders
1179 // remove all permanent binds from the group
1180 // in the DB also remove solo binds that will be replaced with permbinds
1181 // from the new leader
1182 CharacterDatabase
.PExecute(
1183 "DELETE FROM group_instance WHERE leaderguid='%u' AND (permanent = 1 OR "
1184 "instance IN (SELECT instance FROM character_instance WHERE guid = '%u')"
1185 ")", GUID_LOPART(m_leaderGuid
), GUID_LOPART(slot
->guid
)
1188 Player
*player
= sObjectMgr
.GetPlayer(slot
->guid
);
1191 for(uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
1193 for(BoundInstancesMap::iterator itr
= m_boundInstances
[i
].begin(); itr
!= m_boundInstances
[i
].end();)
1195 if(itr
->second
.perm
)
1197 itr
->second
.save
->RemoveGroup(this);
1198 m_boundInstances
[i
].erase(itr
++);
1206 // update the group's solo binds to the new leader
1207 CharacterDatabase
.PExecute("UPDATE group_instance SET leaderGuid='%u' WHERE leaderGuid = '%u'", GUID_LOPART(slot
->guid
), GUID_LOPART(m_leaderGuid
));
1209 // copy the permanent binds from the new leader to the group
1210 // overwriting the solo binds with permanent ones if necessary
1211 // in the DB those have been deleted already
1212 Player::ConvertInstancesToGroup(player
, this, slot
->guid
);
1214 // update the group leader
1215 CharacterDatabase
.PExecute("UPDATE groups SET leaderGuid='%u' WHERE groupId='%u'", GUID_LOPART(slot
->guid
), m_Id
);
1216 CharacterDatabase
.CommitTransaction();
1219 m_leaderGuid
= slot
->guid
;
1220 m_leaderName
= slot
->name
;
1223 void Group::_removeRolls(const uint64
&guid
)
1225 for (Rolls::iterator it
= RollId
.begin(); it
< RollId
.end(); ++it
)
1228 Roll::PlayerVote::iterator itr2
= roll
->playerVote
.find(guid
);
1229 if(itr2
== roll
->playerVote
.end())
1232 if (itr2
->second
== GREED
|| itr2
->second
== DISENCHANT
)
1234 if (itr2
->second
== NEED
)
1236 if (itr2
->second
== PASS
)
1238 if (itr2
->second
!= NOT_VALID
)
1239 --roll
->totalPlayersRolling
;
1241 roll
->playerVote
.erase(itr2
);
1243 CountRollVote(guid
, roll
->itemGUID
, GetMembersCount()-1, MAX_ROLL_TYPE
);
1247 bool Group::_setMembersGroup(const uint64
&guid
, const uint8
&group
)
1249 member_witerator slot
= _getMemberWSlot(guid
);
1250 if(slot
== m_memberSlots
.end())
1253 slot
->group
= group
;
1255 SubGroupCounterIncrease(group
);
1258 CharacterDatabase
.PExecute("UPDATE group_member SET subgroup='%u' WHERE memberGuid='%u'", group
, GUID_LOPART(guid
));
1263 bool Group::_setAssistantFlag(const uint64
&guid
, const bool &state
)
1265 member_witerator slot
= _getMemberWSlot(guid
);
1266 if(slot
== m_memberSlots
.end())
1269 slot
->assistant
= state
;
1271 CharacterDatabase
.PExecute("UPDATE group_member SET assistant='%u' WHERE memberGuid='%u'", (state
==true)?1:0, GUID_LOPART(guid
));
1275 bool Group::_setMainTank(const uint64
&guid
)
1277 member_citerator slot
= _getMemberCSlot(guid
);
1278 if(slot
== m_memberSlots
.end())
1281 if(m_mainAssistant
== guid
)
1282 _setMainAssistant(0);
1285 CharacterDatabase
.PExecute("UPDATE groups SET mainTank='%u' WHERE groupId='%u'", GUID_LOPART(m_mainTank
), m_Id
);
1289 bool Group::_setMainAssistant(const uint64
&guid
)
1291 member_witerator slot
= _getMemberWSlot(guid
);
1292 if(slot
== m_memberSlots
.end())
1295 if(m_mainTank
== guid
)
1297 m_mainAssistant
= guid
;
1299 CharacterDatabase
.PExecute("UPDATE groups SET mainAssistant='%u' WHERE groupId='%u'", GUID_LOPART(m_mainAssistant
), m_Id
);
1303 bool Group::SameSubGroup(Player
const* member1
, Player
const* member2
) const
1305 if(!member1
|| !member2
)
1307 if (member1
->GetGroup() != this || member2
->GetGroup() != this)
1310 return member1
->GetSubGroup() == member2
->GetSubGroup();
1313 // allows setting subgroup for offline members
1314 void Group::ChangeMembersGroup(const uint64
&guid
, const uint8
&group
)
1318 Player
*player
= sObjectMgr
.GetPlayer(guid
);
1323 prevSubGroup
= GetMemberGroup(guid
);
1325 SubGroupCounterDecrease(prevSubGroup
);
1327 if(_setMembersGroup(guid
, group
))
1331 // This methods handles itself groupcounter decrease
1332 ChangeMembersGroup(player
, group
);
1335 // only for online members
1336 void Group::ChangeMembersGroup(Player
*player
, const uint8
&group
)
1338 if(!player
|| !isRaidGroup())
1340 if(_setMembersGroup(player
->GetGUID(), group
))
1342 uint8 prevSubGroup
= player
->GetSubGroup();
1343 if( player
->GetGroup() == this )
1344 player
->GetGroupRef().setSubGroup(group
);
1345 //if player is in BG raid, it is possible that he is also in normal raid - and that normal raid is stored in m_originalGroup reference
1348 prevSubGroup
= player
->GetOriginalSubGroup();
1349 player
->GetOriginalGroupRef().setSubGroup(group
);
1351 SubGroupCounterDecrease(prevSubGroup
);
1357 void Group::UpdateLooterGuid( Creature
* creature
, bool ifneed
)
1359 switch (GetLootMethod())
1365 // round robin style looting applies for all low
1366 // quality items in each loot method except free for all and master loot
1370 member_citerator guid_itr
= _getMemberCSlot(GetLooterGuid());
1371 if(guid_itr
!= m_memberSlots
.end())
1375 // not update if only update if need and ok
1376 Player
* looter
= ObjectAccessor::FindPlayer(guid_itr
->guid
);
1377 if(looter
&& looter
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
1383 // search next after current
1384 if(guid_itr
!= m_memberSlots
.end())
1386 for(member_citerator itr
= guid_itr
; itr
!= m_memberSlots
.end(); ++itr
)
1388 if(Player
* pl
= ObjectAccessor::FindPlayer(itr
->guid
))
1390 if (pl
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
1392 bool refresh
= pl
->GetLootGUID() == creature
->GetGUID();
1394 //if(refresh) // update loot for new looter
1395 // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1396 SetLooterGuid(pl
->GetGUID());
1398 if(refresh
) // update loot for new looter
1399 pl
->SendLoot(creature
->GetGUID(), LOOT_CORPSE
);
1406 // search from start
1407 for(member_citerator itr
= m_memberSlots
.begin(); itr
!= guid_itr
; ++itr
)
1409 if(Player
* pl
= ObjectAccessor::FindPlayer(itr
->guid
))
1411 if (pl
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
1413 bool refresh
= pl
->GetLootGUID()==creature
->GetGUID();
1415 //if(refresh) // update loot for new looter
1416 // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1417 SetLooterGuid(pl
->GetGUID());
1419 if(refresh
) // update loot for new looter
1420 pl
->SendLoot(creature
->GetGUID(), LOOT_CORPSE
);
1430 uint32
Group::CanJoinBattleGroundQueue(BattleGround
const* bgOrTemplate
, BattleGroundQueueTypeId bgQueueTypeId
, uint32 MinPlayerCount
, uint32 MaxPlayerCount
, bool isRated
, uint32 arenaSlot
)
1432 // check for min / max count
1433 uint32 memberscount
= GetMembersCount();
1434 if(memberscount
< MinPlayerCount
)
1435 return BG_JOIN_ERR_GROUP_NOT_ENOUGH
;
1436 if(memberscount
> MaxPlayerCount
)
1437 return BG_JOIN_ERR_GROUP_TOO_MANY
;
1439 // get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.)
1440 Player
* reference
= GetFirstMember()->getSource();
1441 // no reference found, can't join this way
1443 return BG_JOIN_ERR_OFFLINE_MEMBER
;
1445 PvPDifficultyEntry
const* bracketEntry
= GetBattlegroundBracketByLevel(bgOrTemplate
->GetMapId(),reference
->getLevel());
1447 return BG_JOIN_ERR_OFFLINE_MEMBER
;
1449 uint32 arenaTeamId
= reference
->GetArenaTeamId(arenaSlot
);
1450 uint32 team
= reference
->GetTeam();
1452 // check every member of the group to be able to join
1453 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1455 Player
*member
= itr
->getSource();
1456 // offline member? don't let join
1458 return BG_JOIN_ERR_OFFLINE_MEMBER
;
1459 // don't allow cross-faction join as group
1460 if(member
->GetTeam() != team
)
1461 return BG_JOIN_ERR_MIXED_FACTION
;
1462 // not in the same battleground level braket, don't let join
1463 PvPDifficultyEntry
const* memberBracketEntry
= GetBattlegroundBracketByLevel(bracketEntry
->mapId
,member
->getLevel());
1464 if(memberBracketEntry
!= bracketEntry
)
1465 return BG_JOIN_ERR_MIXED_LEVELS
;
1466 // don't let join rated matches if the arena team id doesn't match
1467 if(isRated
&& member
->GetArenaTeamId(arenaSlot
) != arenaTeamId
)
1468 return BG_JOIN_ERR_MIXED_ARENATEAM
;
1469 // don't let join if someone from the group is already in that bg queue
1470 if(member
->InBattleGroundQueueForBattleGroundQueueType(bgQueueTypeId
))
1471 return BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE
;
1472 // check for deserter debuff in case not arena queue
1473 if(bgOrTemplate
->GetTypeID() != BATTLEGROUND_AA
&& !member
->CanJoinToBattleground())
1474 return BG_JOIN_ERR_GROUP_DESERTER
;
1475 // check if member can join any more battleground queues
1476 if(!member
->HasFreeBattleGroundQueueId())
1477 return BG_JOIN_ERR_ALL_QUEUES_USED
;
1479 return BG_JOIN_ERR_OK
;
1482 //===================================================
1483 //============== Roll ===============================
1484 //===================================================
1486 void Roll::targetObjectBuildLink()
1488 // called from link()
1489 getTarget()->addLootValidatorRef(this);
1492 void Group::SetDungeonDifficulty(Difficulty difficulty
)
1494 m_dungeonDifficulty
= difficulty
;
1496 CharacterDatabase
.PExecute("UPDATE groups SET difficulty = %u WHERE groupId='%u'", m_dungeonDifficulty
, m_Id
);
1498 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1500 Player
*player
= itr
->getSource();
1501 if(!player
->GetSession() || player
->getLevel() < LEVELREQUIREMENT_HEROIC
)
1503 player
->SetDungeonDifficulty(difficulty
);
1504 player
->SendDungeonDifficulty(true);
1508 void Group::SetRaidDifficulty(Difficulty difficulty
)
1510 m_raidDifficulty
= difficulty
;
1512 CharacterDatabase
.PExecute("UPDATE groups SET raiddifficulty = %u WHERE groupId='%u'", m_raidDifficulty
, m_Id
);
1514 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1516 Player
*player
= itr
->getSource();
1517 if(!player
->GetSession() || player
->getLevel() < LEVELREQUIREMENT_HEROIC
)
1519 player
->SetRaidDifficulty(difficulty
);
1520 player
->SendRaidDifficulty(true);
1524 bool Group::InCombatToInstance(uint32 instanceId
)
1526 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1528 Player
*pPlayer
= itr
->getSource();
1529 if(pPlayer
->getAttackers().size() && pPlayer
->GetInstanceId() == instanceId
)
1535 void Group::ResetInstances(uint8 method
, bool isRaid
, Player
* SendMsgTo
)
1540 // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_DISBAND
1542 // we assume that when the difficulty changes, all instances that can be reset will be
1543 Difficulty diff
= GetDifficulty(isRaid
);
1545 for(BoundInstancesMap::iterator itr
= m_boundInstances
[diff
].begin(); itr
!= m_boundInstances
[diff
].end();)
1547 InstanceSave
*p
= itr
->second
.save
;
1548 const MapEntry
*entry
= sMapStore
.LookupEntry(itr
->first
);
1549 if (!entry
|| entry
->IsRaid() != isRaid
|| (!p
->CanReset() && method
!= INSTANCE_RESET_GROUP_DISBAND
))
1555 if(method
== INSTANCE_RESET_ALL
)
1557 // the "reset all instances" method can only reset normal maps
1558 if (entry
->map_type
== MAP_RAID
|| diff
== DUNGEON_DIFFICULTY_HEROIC
)
1565 bool isEmpty
= true;
1566 // if the map is loaded, reset it
1567 Map
*map
= sMapMgr
.FindMap(p
->GetMapId(), p
->GetInstanceId());
1568 if(map
&& map
->IsDungeon() && !(method
== INSTANCE_RESET_GROUP_DISBAND
&& !p
->CanReset()))
1569 isEmpty
= ((InstanceMap
*)map
)->Reset(method
);
1574 SendMsgTo
->SendResetInstanceSuccess(p
->GetMapId());
1576 SendMsgTo
->SendResetInstanceFailed(0, p
->GetMapId());
1579 if(isEmpty
|| method
== INSTANCE_RESET_GROUP_DISBAND
|| method
== INSTANCE_RESET_CHANGE_DIFFICULTY
)
1581 // do not reset the instance, just unbind if others are permanently bound to it
1585 CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p
->GetInstanceId());
1586 // i don't know for sure if hash_map iterators
1587 m_boundInstances
[diff
].erase(itr
);
1588 itr
= m_boundInstances
[diff
].begin();
1589 // this unloads the instance save unless online players are bound to it
1590 // (eg. permanent binds or GM solo binds)
1591 p
->RemoveGroup(this);
1598 InstanceGroupBind
* Group::GetBoundInstance(Player
* player
)
1600 uint32 mapid
= player
->GetMapId();
1601 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(mapid
);
1605 Difficulty difficulty
= player
->GetDifficulty(mapEntry
->IsRaid());
1607 // some instances only have one difficulty
1608 MapDifficulty
const* mapDiff
= GetMapDifficultyData(mapid
,difficulty
);
1610 difficulty
= DUNGEON_DIFFICULTY_NORMAL
;
1612 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
1613 if(itr
!= m_boundInstances
[difficulty
].end())
1614 return &itr
->second
;
1619 InstanceGroupBind
* Group::GetBoundInstance(Map
* aMap
)
1621 // Currently spawn numbering not different from map difficulty
1622 Difficulty difficulty
= GetDifficulty(aMap
->IsRaid());
1624 // some instances only have one difficulty
1625 MapDifficulty
const* mapDiff
= GetMapDifficultyData(aMap
->GetId(),difficulty
);
1629 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(aMap
->GetId());
1630 if(itr
!= m_boundInstances
[difficulty
].end())
1631 return &itr
->second
;
1636 InstanceGroupBind
* Group::BindToInstance(InstanceSave
*save
, bool permanent
, bool load
)
1638 if(save
&& !isBGGroup())
1640 InstanceGroupBind
& bind
= m_boundInstances
[save
->GetDifficulty()][save
->GetMapId()];
1643 // when a boss is killed or when copying the players's binds to the group
1644 if(permanent
!= bind
.perm
|| save
!= bind
.save
)
1646 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());
1649 CharacterDatabase
.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", GUID_LOPART(GetLeaderGUID()), save
->GetInstanceId(), permanent
);
1651 if(bind
.save
!= save
)
1654 bind
.save
->RemoveGroup(this);
1655 save
->AddGroup(this);
1659 bind
.perm
= permanent
;
1661 sLog
.outDebug("Group::BindToInstance: %d is now bound to map %d, instance %d, difficulty %d", GUID_LOPART(GetLeaderGUID()), save
->GetMapId(), save
->GetInstanceId(), save
->GetDifficulty());
1668 void Group::UnbindInstance(uint32 mapid
, uint8 difficulty
, bool unload
)
1670 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
1671 if(itr
!= m_boundInstances
[difficulty
].end())
1674 CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", GUID_LOPART(GetLeaderGUID()), itr
->second
.save
->GetInstanceId());
1675 itr
->second
.save
->RemoveGroup(this); // save can become invalid
1676 m_boundInstances
[difficulty
].erase(itr
);
1680 void Group::_homebindIfInstance(Player
*player
)
1682 if(player
&& !player
->isGameMaster() && sMapStore
.LookupEntry(player
->GetMapId())->IsDungeon())
1684 // leaving the group in an instance, the homebind timer is started
1685 // unless the player is permanently saved to the instance
1686 InstanceSave
*save
= sInstanceSaveMgr
.GetInstanceSave(player
->GetInstanceId());
1687 InstancePlayerBind
*playerBind
= save
? player
->GetBoundInstance(save
->GetMapId(), save
->GetDifficulty()) : NULL
;
1688 if(!playerBind
|| !playerBind
->perm
)
1689 player
->m_InstanceValid
= false;