2 * Copyright (C) 2005-2009 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"
28 #include "ObjectAccessor.h"
29 #include "BattleGround.h"
30 #include "MapManager.h"
31 #include "InstanceSaveMgr.h"
32 #include "MapInstanced.h"
40 m_groupType
= (GroupType
)0;
42 m_lootMethod
= (LootMethod
)0;
44 m_lootThreshold
= ITEM_QUALITY_UNCOMMON
;
45 m_subGroupsCounts
= NULL
;
47 for (int i
=0; i
<TARGETICONCOUNT
; ++i
)
55 sLog
.outDebug("Group::~Group: battleground group being deleted.");
56 if(m_bgGroup
->GetBgRaid(ALLIANCE
) == this) m_bgGroup
->SetBgRaid(ALLIANCE
, NULL
);
57 else if(m_bgGroup
->GetBgRaid(HORDE
) == this) m_bgGroup
->SetBgRaid(HORDE
, NULL
);
58 else sLog
.outError("Group::~Group: battleground group is not linked to the correct battleground.");
61 while(!RollId
.empty())
69 // it is undefined whether objectmgr (which stores the groups) or instancesavemgr
70 // will be unloaded first so we must be prepared for both cases
71 // this may unload some instance saves
72 for(uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
73 for(BoundInstancesMap::iterator itr2
= m_boundInstances
[i
].begin(); itr2
!= m_boundInstances
[i
].end(); ++itr2
)
74 itr2
->second
.save
->RemoveGroup(this);
76 // Sub group counters clean up
77 if (m_subGroupsCounts
)
78 delete[] m_subGroupsCounts
;
81 bool Group::Create(const uint64
&guid
, const char * name
)
86 m_groupType
= isBGGroup() ? GROUPTYPE_RAID
: GROUPTYPE_NORMAL
;
88 if (m_groupType
== GROUPTYPE_RAID
)
89 _initRaidSubGroupsCounter();
91 m_lootMethod
= GROUP_LOOT
;
92 m_lootThreshold
= ITEM_QUALITY_UNCOMMON
;
95 m_dungeonDifficulty
= DUNGEON_DIFFICULTY_NORMAL
;
96 m_raidDifficulty
= RAID_DIFFICULTY_10MAN_NORMAL
;
99 Player
*leader
= objmgr
.GetPlayer(guid
);
102 m_dungeonDifficulty
= leader
->GetDungeonDifficulty();
103 m_raidDifficulty
= leader
->GetRaidDifficulty();
106 Player::ConvertInstancesToGroup(leader
, this, guid
);
108 // store group in database
109 CharacterDatabase
.BeginTransaction();
110 CharacterDatabase
.PExecute("DELETE FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid
));
111 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid
));
112 CharacterDatabase
.PExecute("INSERT INTO groups (leaderGuid,mainTank,mainAssistant,lootMethod,looterGuid,lootThreshold,icon1,icon2,icon3,icon4,icon5,icon6,icon7,icon8,isRaid,difficulty,raiddifficulty) "
113 "VALUES ('%u','%u','%u','%u','%u','%u','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','%u','%u','%u')",
114 GUID_LOPART(m_leaderGuid
), GUID_LOPART(m_mainTank
), GUID_LOPART(m_mainAssistant
), uint32(m_lootMethod
),
115 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
);
118 if(!AddMember(guid
, name
))
121 if(!isBGGroup()) CharacterDatabase
.CommitTransaction();
126 bool Group::LoadGroupFromDB(const uint64
&leaderGuid
, QueryResult
*result
, bool loadMembers
)
131 bool external
= true;
135 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
136 result
= CharacterDatabase
.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty, raiddifficulty FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid
));
141 m_leaderGuid
= leaderGuid
;
143 // group leader not exist
144 if(!objmgr
.GetPlayerNameByGUID(m_leaderGuid
, m_leaderName
))
146 if(!external
) delete result
;
150 m_groupType
= (*result
)[13].GetBool() ? GROUPTYPE_RAID
: GROUPTYPE_NORMAL
;
152 if (m_groupType
== GROUPTYPE_RAID
)
153 _initRaidSubGroupsCounter();
155 uint32 diff
= (*result
)[14].GetUInt8();
156 if (diff
>= MAX_DUNGEON_DIFFICULTY
)
157 diff
= DUNGEON_DIFFICULTY_NORMAL
;
158 m_dungeonDifficulty
= Difficulty(diff
);
160 uint32 r_diff
= (*result
)[15].GetUInt8();
161 if (r_diff
>= MAX_RAID_DIFFICULTY
)
162 r_diff
= RAID_DIFFICULTY_10MAN_NORMAL
;
163 m_raidDifficulty
= Difficulty(r_diff
);
165 m_mainTank
= (*result
)[0].GetUInt64();
166 m_mainAssistant
= (*result
)[1].GetUInt64();
167 m_lootMethod
= (LootMethod
)(*result
)[2].GetUInt8();
168 m_looterGuid
= MAKE_NEW_GUID((*result
)[3].GetUInt32(), 0, HIGHGUID_PLAYER
);
169 m_lootThreshold
= (ItemQualities
)(*result
)[4].GetUInt16();
171 for(int i
=0; i
<TARGETICONCOUNT
; ++i
)
172 m_targetIcons
[i
] = (*result
)[5+i
].GetUInt64();
173 if(!external
) delete result
;
177 result
= CharacterDatabase
.PQuery("SELECT memberGuid, assistant, subgroup FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid
));
183 LoadMemberFromDB((*result
)[0].GetUInt32(), (*result
)[2].GetUInt8(), (*result
)[1].GetBool());
184 } while( result
->NextRow() );
187 if(GetMembersCount() < 2)
194 bool Group::LoadMemberFromDB(uint32 guidLow
, uint8 subgroup
, bool assistant
)
197 member
.guid
= MAKE_NEW_GUID(guidLow
, 0, HIGHGUID_PLAYER
);
199 // skip non-existed member
200 if(!objmgr
.GetPlayerNameByGUID(member
.guid
, member
.name
))
203 member
.group
= subgroup
;
204 member
.assistant
= assistant
;
205 m_memberSlots
.push_back(member
);
207 SubGroupCounterIncrease(subgroup
);
212 void Group::ConvertToRaid()
214 m_groupType
= GROUPTYPE_RAID
;
216 _initRaidSubGroupsCounter();
219 CharacterDatabase
.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid
));
222 // update quest related GO states (quest activity dependent from raid membership)
223 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
224 if(Player
* player
= objmgr
.GetPlayer(citr
->guid
))
225 player
->UpdateForQuestWorldObjects();
228 bool Group::AddInvite(Player
*player
)
230 if( !player
|| player
->GetGroupInvite() )
232 Group
* group
= player
->GetGroup();
233 if( group
&& group
->isBGGroup() )
234 group
= player
->GetOriginalGroup();
238 RemoveInvite(player
);
240 m_invitees
.insert(player
);
242 player
->SetGroupInvite(this);
247 bool Group::AddLeaderInvite(Player
*player
)
249 if(!AddInvite(player
))
252 m_leaderGuid
= player
->GetGUID();
253 m_leaderName
= player
->GetName();
257 uint32
Group::RemoveInvite(Player
*player
)
259 m_invitees
.erase(player
);
261 player
->SetGroupInvite(NULL
);
262 return GetMembersCount();
265 void Group::RemoveAllInvites()
267 for(InvitesList::iterator itr
=m_invitees
.begin(); itr
!=m_invitees
.end(); ++itr
)
268 (*itr
)->SetGroupInvite(NULL
);
273 Player
* Group::GetInvited(const uint64
& guid
) const
275 for(InvitesList::const_iterator itr
= m_invitees
.begin(); itr
!= m_invitees
.end(); ++itr
)
277 if((*itr
)->GetGUID() == guid
)
283 Player
* Group::GetInvited(const std::string
& name
) const
285 for(InvitesList::const_iterator itr
= m_invitees
.begin(); itr
!= m_invitees
.end(); ++itr
)
287 if((*itr
)->GetName() == name
)
293 bool Group::AddMember(const uint64
&guid
, const char* name
)
295 if(!_addMember(guid
, name
))
299 Player
*player
= objmgr
.GetPlayer(guid
);
302 if(!IsLeader(player
->GetGUID()) && !isBGGroup())
304 // reset the new member's instances, unless he is currently in one of them
305 // including raid/heroic instances that they are not permanently bound to!
306 player
->ResetInstances(INSTANCE_RESET_GROUP_JOIN
,false);
307 player
->ResetInstances(INSTANCE_RESET_GROUP_JOIN
,true);
309 if (player
->getLevel() >= LEVELREQUIREMENT_HEROIC
)
311 if (player
->GetDungeonDifficulty() != GetDungeonDifficulty())
313 player
->SetDungeonDifficulty(GetDungeonDifficulty());
314 player
->SendDungeonDifficulty(true);
316 if (player
->GetRaidDifficulty() != GetRaidDifficulty())
318 player
->SetRaidDifficulty(GetRaidDifficulty());
319 player
->SendRaidDifficulty(true);
323 player
->SetGroupUpdateFlag(GROUP_UPDATE_FULL
);
324 UpdatePlayerOutOfRange(player
);
326 // quest related GO state dependent from raid membership
328 player
->UpdateForQuestWorldObjects();
334 uint32
Group::RemoveMember(const uint64
&guid
, const uint8
&method
)
336 // remove member and change leader (if need) only if strong more 2 members _before_ member remove
337 if(GetMembersCount() > (isBGGroup() ? 1 : 2)) // in BG group case allow 1 members group
339 bool leaderChanged
= _removeMember(guid
);
341 if(Player
*player
= objmgr
.GetPlayer( guid
))
343 // quest related GO state dependent from raid membership
345 player
->UpdateForQuestWorldObjects();
351 data
.Initialize( SMSG_GROUP_UNINVITE
, 0 );
352 player
->GetSession()->SendPacket( &data
);
355 //we already removed player from group and in player->GetGroup() is his original group!
356 if( Group
* group
= player
->GetGroup() )
362 data
.Initialize(SMSG_GROUP_LIST
, 24);
363 data
<< uint64(0) << uint64(0) << uint64(0);
364 player
->GetSession()->SendPacket(&data
);
367 _homebindIfInstance(player
);
372 WorldPacket
data(SMSG_GROUP_SET_LEADER
, (m_memberSlots
.front().name
.size()+1));
373 data
<< m_memberSlots
.front().name
;
374 BroadcastPacket(&data
, true);
379 // if group before remove <= 2 disband it
383 return m_memberSlots
.size();
386 void Group::ChangeLeader(const uint64
&guid
)
388 member_citerator slot
= _getMemberCSlot(guid
);
390 if(slot
== m_memberSlots
.end())
395 WorldPacket
data(SMSG_GROUP_SET_LEADER
, slot
->name
.size()+1);
397 BroadcastPacket(&data
, true);
401 void Group::Disband(bool hideDestroy
)
405 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
407 player
= objmgr
.GetPlayer(citr
->guid
);
411 //we cannot call _removeMember because it would invalidate member iterator
412 //if we are removing player from battleground raid
414 player
->RemoveFromBattleGroundRaid();
417 //we can remove player who is in battleground from his original group
418 if( player
->GetOriginalGroup() == this )
419 player
->SetOriginalGroup(NULL
);
421 player
->SetGroup(NULL
);
424 // quest related GO state dependent from raid membership
426 player
->UpdateForQuestWorldObjects();
428 if(!player
->GetSession())
434 data
.Initialize(SMSG_GROUP_DESTROYED
, 0);
435 player
->GetSession()->SendPacket(&data
);
438 //we already removed player from group and in player->GetGroup() is his original group, send update
439 if( Group
* group
= player
->GetGroup() )
445 data
.Initialize(SMSG_GROUP_LIST
, 24);
446 data
<< uint64(0) << uint64(0) << uint64(0);
447 player
->GetSession()->SendPacket(&data
);
450 _homebindIfInstance(player
);
453 m_memberSlots
.clear();
459 CharacterDatabase
.BeginTransaction();
460 CharacterDatabase
.PExecute("DELETE FROM groups WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid
));
461 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid
));
462 CharacterDatabase
.CommitTransaction();
463 ResetInstances(INSTANCE_RESET_GROUP_DISBAND
, false, NULL
);
464 ResetInstances(INSTANCE_RESET_GROUP_DISBAND
, true, NULL
);
471 /*********************************************************/
472 /*** LOOT SYSTEM ***/
473 /*********************************************************/
475 void Group::SendLootStartRoll(uint32 CountDown
, const Roll
&r
)
477 WorldPacket
data(SMSG_LOOT_START_ROLL
, (8+4+4+4+4+4));
478 data
<< uint64(r
.itemGUID
); // guid of rolled item
479 data
<< uint32(r
.totalPlayersRolling
); // maybe the number of players rolling for it???
480 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
481 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
482 data
<< uint32(r
.itemRandomPropId
); // item random property ID
483 data
<< uint32(CountDown
); // the countdown time to choose "need" or "greed"
485 for (Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
487 Player
*p
= objmgr
.GetPlayer(itr
->first
);
488 if(!p
|| !p
->GetSession())
491 if(itr
->second
!= NOT_VALID
)
492 p
->GetSession()->SendPacket( &data
);
496 void Group::SendLootRoll(const uint64
& SourceGuid
, const uint64
& TargetGuid
, uint8 RollNumber
, uint8 RollType
, const Roll
&r
)
498 WorldPacket
data(SMSG_LOOT_ROLL
, (8+4+8+4+4+4+1+1));
499 data
<< uint64(SourceGuid
); // guid of the item rolled
500 data
<< uint32(0); // unknown, maybe amount of players
501 data
<< uint64(TargetGuid
);
502 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
503 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
504 data
<< uint32(r
.itemRandomPropId
); // Item random property ID
505 data
<< uint8(RollNumber
); // 0: "Need for: [item name]" > 127: "you passed on: [item name]" Roll number
506 data
<< uint8(RollType
); // 0: "Need for: [item name]" 0: "You have selected need for [item name] 1: need roll 2: greed roll
507 data
<< uint8(0); // 2.4.0
509 for( Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
511 Player
*p
= objmgr
.GetPlayer(itr
->first
);
512 if(!p
|| !p
->GetSession())
515 if(itr
->second
!= NOT_VALID
)
516 p
->GetSession()->SendPacket( &data
);
520 void Group::SendLootRollWon(const uint64
& SourceGuid
, const uint64
& TargetGuid
, uint8 RollNumber
, uint8 RollType
, const Roll
&r
)
522 WorldPacket
data(SMSG_LOOT_ROLL_WON
, (8+4+4+4+4+8+1+1));
523 data
<< uint64(SourceGuid
); // guid of the item rolled
524 data
<< uint32(0); // unknown, maybe amount of players
525 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
526 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
527 data
<< uint32(r
.itemRandomPropId
); // Item random property
528 data
<< uint64(TargetGuid
); // guid of the player who won.
529 data
<< uint8(RollNumber
); // rollnumber realted to SMSG_LOOT_ROLL
530 data
<< uint8(RollType
); // Rolltype related to SMSG_LOOT_ROLL
532 for( Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
534 Player
*p
= objmgr
.GetPlayer(itr
->first
);
535 if(!p
|| !p
->GetSession())
538 if(itr
->second
!= NOT_VALID
)
539 p
->GetSession()->SendPacket( &data
);
543 void Group::SendLootAllPassed(uint32 NumberOfPlayers
, const Roll
&r
)
545 WorldPacket
data(SMSG_LOOT_ALL_PASSED
, (8+4+4+4+4));
546 data
<< uint64(r
.itemGUID
); // Guid of the item rolled
547 data
<< uint32(NumberOfPlayers
); // The number of players rolling for it???
548 data
<< uint32(r
.itemid
); // The itemEntryId for the item that shall be rolled for
549 data
<< uint32(r
.itemRandomPropId
); // Item random property ID
550 data
<< uint32(r
.itemRandomSuffix
); // Item random suffix ID
552 for( Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
554 Player
*p
= objmgr
.GetPlayer(itr
->first
);
555 if(!p
|| !p
->GetSession())
558 if(itr
->second
!= NOT_VALID
)
559 p
->GetSession()->SendPacket( &data
);
563 void Group::GroupLoot(const uint64
& playerGUID
, Loot
*loot
, Creature
*creature
)
565 std::vector
<LootItem
>::iterator i
;
566 ItemPrototype
const *item
;
568 Player
*player
= objmgr
.GetPlayer(playerGUID
);
569 Group
*group
= player
->GetGroup();
571 for (i
=loot
->items
.begin(); i
!= loot
->items
.end(); ++i
, ++itemSlot
)
573 item
= objmgr
.GetItemPrototype(i
->itemid
);
576 //sLog.outDebug("Group::GroupLoot: missing item prototype for item with id: %d", i->itemid);
580 //roll for over-threshold item if it's one-player loot
581 if (item
->Quality
>= uint32(m_lootThreshold
) && !i
->freeforall
)
583 uint64 newitemGUID
= MAKE_NEW_GUID(objmgr
.GenerateLowGuid(HIGHGUID_ITEM
),0,HIGHGUID_ITEM
);
584 Roll
* r
=new Roll(newitemGUID
,*i
);
586 //a vector is filled with only near party members
587 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
589 Player
*member
= itr
->getSource();
590 if(!member
|| !member
->GetSession())
592 if ( i
->AllowedForPlayer(member
) )
594 if (member
->IsWithinDist(creature
,sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
),false))
596 r
->playerVote
[member
->GetGUID()] = NOT_EMITED_YET
;
597 ++r
->totalPlayersRolling
;
603 r
->itemSlot
= itemSlot
;
605 group
->SendLootStartRoll(60000, *r
);
607 loot
->items
[itemSlot
].is_blocked
= true;
608 creature
->m_groupLootTimer
= 60000;
609 creature
->lootingGroupLeaderGUID
= GetLeaderGUID();
614 i
->is_underthreshold
=1;
619 void Group::NeedBeforeGreed(const uint64
& playerGUID
, Loot
*loot
, Creature
*creature
)
621 ItemPrototype
const *item
;
622 Player
*player
= objmgr
.GetPlayer(playerGUID
);
623 Group
*group
= player
->GetGroup();
626 for(std::vector
<LootItem
>::iterator i
=loot
->items
.begin(); i
!= loot
->items
.end(); ++i
, ++itemSlot
)
628 item
= objmgr
.GetItemPrototype(i
->itemid
);
630 //only roll for one-player items, not for ones everyone can get
631 if (item
->Quality
>= uint32(m_lootThreshold
) && !i
->freeforall
)
633 uint64 newitemGUID
= MAKE_NEW_GUID(objmgr
.GenerateLowGuid(HIGHGUID_ITEM
),0,HIGHGUID_ITEM
);
634 Roll
* r
=new Roll(newitemGUID
,*i
);
636 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
638 Player
*playerToRoll
= itr
->getSource();
639 if(!playerToRoll
|| !playerToRoll
->GetSession())
642 if (playerToRoll
->CanUseItem(item
) && i
->AllowedForPlayer(playerToRoll
) )
644 if (playerToRoll
->IsWithinDist(creature
,sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
),false))
646 r
->playerVote
[playerToRoll
->GetGUID()] = NOT_EMITED_YET
;
647 ++r
->totalPlayersRolling
;
652 if (r
->totalPlayersRolling
> 0)
655 r
->itemSlot
= itemSlot
;
657 group
->SendLootStartRoll(60000, *r
);
659 loot
->items
[itemSlot
].is_blocked
= true;
669 i
->is_underthreshold
=1;
673 void Group::MasterLoot(const uint64
& playerGUID
, Loot
* /*loot*/, Creature
*creature
)
675 Player
*player
= objmgr
.GetPlayer(playerGUID
);
679 sLog
.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330) player = [%s].", player
->GetName());
681 uint32 real_count
= 0;
683 WorldPacket
data(SMSG_LOOT_MASTER_LIST
, 330);
684 data
<< (uint8
)GetMembersCount();
686 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
688 Player
*looter
= itr
->getSource();
689 if (!looter
->IsInWorld())
692 if (looter
->IsWithinDist(creature
,sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
),false))
694 data
<< looter
->GetGUID();
699 data
.put
<uint8
>(0,real_count
);
701 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
703 Player
*looter
= itr
->getSource();
704 if (looter
->IsWithinDist(creature
,sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
),false))
705 looter
->GetSession()->SendPacket(&data
);
709 void Group::CountRollVote(const uint64
& playerGUID
, const uint64
& Guid
, uint32 NumberOfPlayers
, uint8 Choise
)
711 Rolls::iterator rollI
= GetRoll(Guid
);
712 if (rollI
== RollId
.end())
716 Roll::PlayerVote::iterator itr
= roll
->playerVote
.find(playerGUID
);
717 // this condition means that player joins to the party after roll begins
718 if (itr
== roll
->playerVote
.end())
722 if (roll
->getLoot()->items
.empty())
727 case 0: //Player choose pass
729 SendLootRoll(0, playerGUID
, 128, 128, *roll
);
734 case 1: //player choose Need
736 SendLootRoll(0, playerGUID
, 0, 0, *roll
);
741 case 2: //player choose Greed
743 SendLootRoll(0, playerGUID
, 128, 2, *roll
);
749 if (roll
->totalPass
+ roll
->totalGreed
+ roll
->totalNeed
>= roll
->totalPlayersRolling
)
751 CountTheRoll(rollI
, NumberOfPlayers
);
755 //called when roll timer expires
756 void Group::EndRoll()
759 while(!RollId
.empty())
761 //need more testing here, if rolls disappear
762 itr
= RollId
.begin();
763 CountTheRoll(itr
, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass
767 void Group::CountTheRoll(Rolls::iterator rollI
, uint32 NumberOfPlayers
)
770 if(!roll
->isValid()) // is loot already deleted ?
777 if (roll
->totalNeed
> 0)
779 if(!roll
->playerVote
.empty())
782 uint64 maxguid
= (*roll
->playerVote
.begin()).first
;
785 for( Roll::PlayerVote::const_iterator itr
=roll
->playerVote
.begin(); itr
!=roll
->playerVote
.end(); ++itr
)
787 if (itr
->second
!= NEED
)
790 uint8 randomN
= urand(1, 99);
791 SendLootRoll(0, itr
->first
, randomN
, 1, *roll
);
792 if (maxresul
< randomN
)
794 maxguid
= itr
->first
;
798 SendLootRollWon(0, maxguid
, maxresul
, 1, *roll
);
799 player
= objmgr
.GetPlayer(maxguid
);
801 if(player
&& player
->GetSession())
803 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT
, roll
->itemid
, maxresul
);
805 ItemPosCountVec dest
;
806 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
807 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, roll
->itemid
, item
->count
);
808 if ( msg
== EQUIP_ERR_OK
)
810 item
->is_looted
= true;
811 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
812 --roll
->getLoot()->unlootedCount
;
813 player
->StoreNewItem( dest
, roll
->itemid
, true, item
->randomPropertyId
);
817 item
->is_blocked
= false;
818 player
->SendEquipError( msg
, NULL
, NULL
);
823 else if (roll
->totalGreed
> 0)
825 if(!roll
->playerVote
.empty())
828 uint64 maxguid
= (*roll
->playerVote
.begin()).first
;
831 Roll::PlayerVote::iterator itr
;
832 for (itr
=roll
->playerVote
.begin(); itr
!=roll
->playerVote
.end(); ++itr
)
834 if (itr
->second
!= GREED
)
837 uint8 randomN
= urand(1, 99);
838 SendLootRoll(0, itr
->first
, randomN
, 2, *roll
);
839 if (maxresul
< randomN
)
841 maxguid
= itr
->first
;
845 SendLootRollWon(0, maxguid
, maxresul
, 2, *roll
);
846 player
= objmgr
.GetPlayer(maxguid
);
848 if(player
&& player
->GetSession())
850 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT
, roll
->itemid
, maxresul
);
852 ItemPosCountVec dest
;
853 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
854 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, roll
->itemid
, item
->count
);
855 if ( msg
== EQUIP_ERR_OK
)
857 item
->is_looted
= true;
858 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
859 --roll
->getLoot()->unlootedCount
;
860 player
->StoreNewItem( dest
, roll
->itemid
, true, item
->randomPropertyId
);
864 item
->is_blocked
= false;
865 player
->SendEquipError( msg
, NULL
, NULL
);
872 SendLootAllPassed(NumberOfPlayers
, *roll
);
873 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
874 if(item
) item
->is_blocked
= false;
880 void Group::SetTargetIcon(uint8 id
, uint64 guid
)
882 if(id
>= TARGETICONCOUNT
)
887 for(int i
=0; i
<TARGETICONCOUNT
; ++i
)
888 if( m_targetIcons
[i
] == guid
)
891 m_targetIcons
[id
] = guid
;
893 WorldPacket
data(MSG_RAID_TARGET_UPDATE
, (2+8));
897 BroadcastPacket(&data
, true);
900 void Group::GetDataForXPAtKill(Unit
const* victim
, uint32
& count
,uint32
& sum_level
, Player
* & member_with_max_level
, Player
* & not_gray_member_with_max_level
)
902 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
904 Player
* member
= itr
->getSource();
905 if(!member
|| !member
->isAlive()) // only for alive
908 if(!member
->IsAtGroupRewardDistance(victim
)) // at req. distance
912 sum_level
+= member
->getLevel();
913 if(!member_with_max_level
|| member_with_max_level
->getLevel() < member
->getLevel())
914 member_with_max_level
= member
;
916 uint32 gray_level
= MaNGOS::XP::GetGrayLevel(member
->getLevel());
917 if( victim
->getLevel() > gray_level
&& (!not_gray_member_with_max_level
918 || not_gray_member_with_max_level
->getLevel() < member
->getLevel()))
919 not_gray_member_with_max_level
= member
;
923 void Group::SendTargetIconList(WorldSession
*session
)
928 WorldPacket
data(MSG_RAID_TARGET_UPDATE
, (1+TARGETICONCOUNT
*9));
931 for(int i
=0; i
<TARGETICONCOUNT
; ++i
)
933 if(m_targetIcons
[i
] == 0)
937 data
<< m_targetIcons
[i
];
940 session
->SendPacket(&data
);
943 void Group::SendUpdate()
947 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
949 player
= objmgr
.GetPlayer(citr
->guid
);
950 if(!player
|| !player
->GetSession() || player
->GetGroup() != this )
953 WorldPacket
data(SMSG_GROUP_LIST
, (1+1+1+1+8+4+GetMembersCount()*20));
954 data
<< (uint8
)m_groupType
; // group type
955 data
<< (uint8
)(isBGGroup() ? 1 : 0); // 2.0.x, isBattleGroundGroup?
956 data
<< (uint8
)(citr
->group
); // groupid
957 data
<< (uint8
)(citr
->assistant
?0x01:0); // 0x2 main assist, 0x4 main tank
958 data
<< uint64(0x50000000FFFFFFFELL
); // related to voice chat?
959 data
<< uint32(GetMembersCount()-1);
960 for(member_citerator citr2
= m_memberSlots
.begin(); citr2
!= m_memberSlots
.end(); ++citr2
)
962 if(citr
->guid
== citr2
->guid
)
964 Player
* member
= objmgr
.GetPlayer(citr2
->guid
);
965 uint8 onlineState
= (member
) ? MEMBER_STATUS_ONLINE
: MEMBER_STATUS_OFFLINE
;
966 onlineState
= onlineState
| ((isBGGroup()) ? MEMBER_STATUS_PVP
: 0);
969 data
<< (uint64
)citr2
->guid
;
971 data
<< (uint8
)(onlineState
);
972 data
<< (uint8
)(citr2
->group
); // groupid
973 data
<< (uint8
)(citr2
->assistant
?0x01:0); // 0x2 main assist, 0x4 main tank
976 data
<< uint64(m_leaderGuid
); // leader guid
977 if(GetMembersCount()-1)
979 data
<< (uint8
)m_lootMethod
; // loot method
980 data
<< (uint64
)m_looterGuid
; // looter guid
981 data
<< (uint8
)m_lootThreshold
; // loot threshold
982 data
<< (uint8
)m_dungeonDifficulty
; // Dungeon Difficulty
983 data
<< (uint8
)m_raidDifficulty
; // Raid Difficulty
985 player
->GetSession()->SendPacket( &data
);
989 void Group::UpdatePlayerOutOfRange(Player
* pPlayer
)
991 if(!pPlayer
|| !pPlayer
->IsInWorld())
996 pPlayer
->GetSession()->BuildPartyMemberStatsChangedPacket(pPlayer
, &data
);
998 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1000 player
= itr
->getSource();
1001 if (player
&& player
!= pPlayer
&& !pPlayer
->isVisibleFor(player
,player
->GetViewPoint()))
1002 player
->GetSession()->SendPacket(&data
);
1006 void Group::BroadcastPacket(WorldPacket
*packet
, bool ignorePlayersInBGRaid
, int group
, uint64 ignore
)
1008 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1010 Player
*pl
= itr
->getSource();
1011 if(!pl
|| (ignore
!= 0 && pl
->GetGUID() == ignore
) || (ignorePlayersInBGRaid
&& pl
->GetGroup() != this) )
1014 if (pl
->GetSession() && (group
==-1 || itr
->getSubGroup()==group
))
1015 pl
->GetSession()->SendPacket(packet
);
1019 void Group::BroadcastReadyCheck(WorldPacket
*packet
)
1021 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1023 Player
*pl
= itr
->getSource();
1024 if(pl
&& pl
->GetSession())
1025 if(IsLeader(pl
->GetGUID()) || IsAssistant(pl
->GetGUID()))
1026 pl
->GetSession()->SendPacket(packet
);
1030 void Group::OfflineReadyCheck()
1032 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
1034 Player
*pl
= objmgr
.GetPlayer(citr
->guid
);
1035 if (!pl
|| !pl
->GetSession())
1037 WorldPacket
data(MSG_RAID_READY_CHECK_CONFIRM
, 9);
1040 BroadcastReadyCheck(&data
);
1045 bool Group::_addMember(const uint64
&guid
, const char* name
, bool isAssistant
)
1047 // get first not-full group
1049 if (m_subGroupsCounts
)
1051 bool groupFound
= false;
1052 for (; groupid
< MAXRAIDSIZE
/MAXGROUPSIZE
; ++groupid
)
1054 if (m_subGroupsCounts
[groupid
] < MAXGROUPSIZE
)
1060 // We are raid group and no one slot is free
1065 return _addMember(guid
, name
, isAssistant
, groupid
);
1068 bool Group::_addMember(const uint64
&guid
, const char* name
, bool isAssistant
, uint8 group
)
1076 Player
*player
= objmgr
.GetPlayer(guid
);
1081 member
.group
= group
;
1082 member
.assistant
= isAssistant
;
1083 m_memberSlots
.push_back(member
);
1085 SubGroupCounterIncrease(group
);
1089 player
->SetGroupInvite(NULL
);
1090 //if player is in group and he is being added to BG raid group, then call SetBattleGroundRaid()
1091 if( player
->GetGroup() && isBGGroup() )
1092 player
->SetBattleGroundRaid(this, group
);
1093 //if player is in bg raid and we are adding him to normal group, then call SetOriginalGroup()
1094 else if ( player
->GetGroup() )
1095 player
->SetOriginalGroup(this, group
);
1096 //if player is not in group, then call set group
1098 player
->SetGroup(this, group
);
1099 // if the same group invites the player back, cancel the homebind timer
1100 InstanceGroupBind
*bind
= GetBoundInstance(player
);
1101 if(bind
&& bind
->save
->GetInstanceId() == player
->GetInstanceId())
1102 player
->m_InstanceValid
= true;
1105 if(!isRaidGroup()) // reset targetIcons for non-raid-groups
1107 for(int i
=0; i
<TARGETICONCOUNT
; ++i
)
1108 m_targetIcons
[i
] = 0;
1113 // insert into group table
1114 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
);
1120 bool Group::_removeMember(const uint64
&guid
)
1122 Player
*player
= objmgr
.GetPlayer(guid
);
1125 //if we are removing player from battleground raid
1127 player
->RemoveFromBattleGroundRaid();
1130 //we can remove player who is in battleground from his original group
1131 if( player
->GetOriginalGroup() == this )
1132 player
->SetOriginalGroup(NULL
);
1134 player
->SetGroup(NULL
);
1140 member_witerator slot
= _getMemberWSlot(guid
);
1141 if (slot
!= m_memberSlots
.end())
1143 SubGroupCounterDecrease(slot
->group
);
1145 m_memberSlots
.erase(slot
);
1149 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE memberGuid='%u'", GUID_LOPART(guid
));
1151 if(m_leaderGuid
== guid
) // leader was removed
1153 if(GetMembersCount() > 0)
1154 _setLeader(m_memberSlots
.front().guid
);
1161 void Group::_setLeader(const uint64
&guid
)
1163 member_citerator slot
= _getMemberCSlot(guid
);
1164 if(slot
==m_memberSlots
.end())
1169 // TODO: set a time limit to have this function run rarely cause it can be slow
1170 CharacterDatabase
.BeginTransaction();
1172 // update the group's bound instances when changing leaders
1174 // remove all permanent binds from the group
1175 // in the DB also remove solo binds that will be replaced with permbinds
1176 // from the new leader
1177 CharacterDatabase
.PExecute(
1178 "DELETE FROM group_instance WHERE leaderguid='%u' AND (permanent = 1 OR "
1179 "instance IN (SELECT instance FROM character_instance WHERE guid = '%u')"
1180 ")", GUID_LOPART(m_leaderGuid
), GUID_LOPART(slot
->guid
)
1183 Player
*player
= objmgr
.GetPlayer(slot
->guid
);
1186 for(uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
1188 for(BoundInstancesMap::iterator itr
= m_boundInstances
[i
].begin(); itr
!= m_boundInstances
[i
].end();)
1190 if(itr
->second
.perm
)
1192 itr
->second
.save
->RemoveGroup(this);
1193 m_boundInstances
[i
].erase(itr
++);
1201 // update the group's solo binds to the new leader
1202 CharacterDatabase
.PExecute("UPDATE group_instance SET leaderGuid='%u' WHERE leaderGuid = '%u'", GUID_LOPART(slot
->guid
), GUID_LOPART(m_leaderGuid
));
1204 // copy the permanent binds from the new leader to the group
1205 // overwriting the solo binds with permanent ones if necessary
1206 // in the DB those have been deleted already
1207 Player::ConvertInstancesToGroup(player
, this, slot
->guid
);
1209 // update the group leader
1210 CharacterDatabase
.PExecute("UPDATE groups SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot
->guid
), GUID_LOPART(m_leaderGuid
));
1211 CharacterDatabase
.PExecute("UPDATE group_member SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot
->guid
), GUID_LOPART(m_leaderGuid
));
1212 CharacterDatabase
.CommitTransaction();
1215 m_leaderGuid
= slot
->guid
;
1216 m_leaderName
= slot
->name
;
1219 void Group::_removeRolls(const uint64
&guid
)
1221 for (Rolls::iterator it
= RollId
.begin(); it
< RollId
.end(); ++it
)
1224 Roll::PlayerVote::iterator itr2
= roll
->playerVote
.find(guid
);
1225 if(itr2
== roll
->playerVote
.end())
1228 if (itr2
->second
== GREED
) --roll
->totalGreed
;
1229 if (itr2
->second
== NEED
) --roll
->totalNeed
;
1230 if (itr2
->second
== PASS
) --roll
->totalPass
;
1231 if (itr2
->second
!= NOT_VALID
) --roll
->totalPlayersRolling
;
1233 roll
->playerVote
.erase(itr2
);
1235 CountRollVote(guid
, roll
->itemGUID
, GetMembersCount()-1, 3);
1239 bool Group::_setMembersGroup(const uint64
&guid
, const uint8
&group
)
1241 member_witerator slot
= _getMemberWSlot(guid
);
1242 if(slot
==m_memberSlots
.end())
1245 slot
->group
= group
;
1247 SubGroupCounterIncrease(group
);
1249 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE group_member SET subgroup='%u' WHERE memberGuid='%u'", group
, GUID_LOPART(guid
));
1254 bool Group::_setAssistantFlag(const uint64
&guid
, const bool &state
)
1256 member_witerator slot
= _getMemberWSlot(guid
);
1257 if(slot
==m_memberSlots
.end())
1260 slot
->assistant
= state
;
1261 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE group_member SET assistant='%u' WHERE memberGuid='%u'", (state
==true)?1:0, GUID_LOPART(guid
));
1265 bool Group::_setMainTank(const uint64
&guid
)
1267 member_citerator slot
= _getMemberCSlot(guid
);
1268 if(slot
==m_memberSlots
.end())
1271 if(m_mainAssistant
== guid
)
1272 _setMainAssistant(0);
1274 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE groups SET mainTank='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainTank
), GUID_LOPART(m_leaderGuid
));
1278 bool Group::_setMainAssistant(const uint64
&guid
)
1280 member_witerator slot
= _getMemberWSlot(guid
);
1281 if(slot
==m_memberSlots
.end())
1284 if(m_mainTank
== guid
)
1286 m_mainAssistant
= guid
;
1287 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE groups SET mainAssistant='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainAssistant
), GUID_LOPART(m_leaderGuid
));
1291 bool Group::SameSubGroup(Player
const* member1
, Player
const* member2
) const
1293 if(!member1
|| !member2
) return false;
1294 if (member1
->GetGroup() != this || member2
->GetGroup() != this) return false;
1295 else return member1
->GetSubGroup() == member2
->GetSubGroup();
1298 // allows setting subgroup for offline members
1299 void Group::ChangeMembersGroup(const uint64
&guid
, const uint8
&group
)
1303 Player
*player
= objmgr
.GetPlayer(guid
);
1308 prevSubGroup
= GetMemberGroup(guid
);
1310 SubGroupCounterDecrease(prevSubGroup
);
1312 if(_setMembersGroup(guid
, group
))
1316 // This methods handles itself groupcounter decrease
1317 ChangeMembersGroup(player
, group
);
1320 // only for online members
1321 void Group::ChangeMembersGroup(Player
*player
, const uint8
&group
)
1323 if(!player
|| !isRaidGroup())
1325 if(_setMembersGroup(player
->GetGUID(), group
))
1327 uint8 prevSubGroup
= player
->GetSubGroup();
1328 if( player
->GetGroup() == this )
1329 player
->GetGroupRef().setSubGroup(group
);
1330 //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
1333 prevSubGroup
= player
->GetOriginalSubGroup();
1334 player
->GetOriginalGroupRef().setSubGroup(group
);
1336 SubGroupCounterDecrease(prevSubGroup
);
1342 void Group::UpdateLooterGuid( Creature
* creature
, bool ifneed
)
1344 switch (GetLootMethod())
1350 // round robin style looting applies for all low
1351 // quality items in each loot method except free for all and master loot
1355 member_citerator guid_itr
= _getMemberCSlot(GetLooterGuid());
1356 if(guid_itr
!= m_memberSlots
.end())
1360 // not update if only update if need and ok
1361 Player
* looter
= ObjectAccessor::FindPlayer(guid_itr
->guid
);
1362 if(looter
&& looter
->IsWithinDist(creature
,sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
),false))
1368 // search next after current
1369 if(guid_itr
!= m_memberSlots
.end())
1371 for(member_citerator itr
= guid_itr
; itr
!= m_memberSlots
.end(); ++itr
)
1373 if(Player
* pl
= ObjectAccessor::FindPlayer(itr
->guid
))
1375 if (pl
->IsWithinDist(creature
,sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
),false))
1377 bool refresh
= pl
->GetLootGUID()==creature
->GetGUID();
1379 //if(refresh) // update loot for new looter
1380 // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1381 SetLooterGuid(pl
->GetGUID());
1383 if(refresh
) // update loot for new looter
1384 pl
->SendLoot(creature
->GetGUID(),LOOT_CORPSE
);
1391 // search from start
1392 for(member_citerator itr
= m_memberSlots
.begin(); itr
!= guid_itr
; ++itr
)
1394 if(Player
* pl
= ObjectAccessor::FindPlayer(itr
->guid
))
1396 if (pl
->IsWithinDist(creature
,sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
),false))
1398 bool refresh
= pl
->GetLootGUID()==creature
->GetGUID();
1400 //if(refresh) // update loot for new looter
1401 // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1402 SetLooterGuid(pl
->GetGUID());
1404 if(refresh
) // update loot for new looter
1405 pl
->SendLoot(creature
->GetGUID(),LOOT_CORPSE
);
1415 uint32
Group::CanJoinBattleGroundQueue(BattleGroundTypeId bgTypeId
, BattleGroundQueueTypeId bgQueueTypeId
, uint32 MinPlayerCount
, uint32 MaxPlayerCount
, bool isRated
, uint32 arenaSlot
)
1417 // check for min / max count
1418 uint32 memberscount
= GetMembersCount();
1419 if(memberscount
< MinPlayerCount
)
1420 return BG_JOIN_ERR_GROUP_NOT_ENOUGH
;
1421 if(memberscount
> MaxPlayerCount
)
1422 return BG_JOIN_ERR_GROUP_TOO_MANY
;
1424 // get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.)
1425 Player
* reference
= GetFirstMember()->getSource();
1426 // no reference found, can't join this way
1428 return BG_JOIN_ERR_OFFLINE_MEMBER
;
1430 BGQueueIdBasedOnLevel queue_id
= reference
->GetBattleGroundQueueIdFromLevel(bgTypeId
);
1431 uint32 arenaTeamId
= reference
->GetArenaTeamId(arenaSlot
);
1432 uint32 team
= reference
->GetTeam();
1434 // check every member of the group to be able to join
1435 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1437 Player
*member
= itr
->getSource();
1438 // offline member? don't let join
1440 return BG_JOIN_ERR_OFFLINE_MEMBER
;
1441 // don't allow cross-faction join as group
1442 if(member
->GetTeam() != team
)
1443 return BG_JOIN_ERR_MIXED_FACTION
;
1444 // not in the same battleground level braket, don't let join
1445 if(member
->GetBattleGroundQueueIdFromLevel(bgTypeId
) != queue_id
)
1446 return BG_JOIN_ERR_MIXED_LEVELS
;
1447 // don't let join rated matches if the arena team id doesn't match
1448 if(isRated
&& member
->GetArenaTeamId(arenaSlot
) != arenaTeamId
)
1449 return BG_JOIN_ERR_MIXED_ARENATEAM
;
1450 // don't let join if someone from the group is already in that bg queue
1451 if(member
->InBattleGroundQueueForBattleGroundQueueType(bgQueueTypeId
))
1452 return BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE
;
1453 // check for deserter debuff in case not arena queue
1454 if(bgTypeId
!= BATTLEGROUND_AA
&& !member
->CanJoinToBattleground())
1455 return BG_JOIN_ERR_GROUP_DESERTER
;
1456 // check if member can join any more battleground queues
1457 if(!member
->HasFreeBattleGroundQueueId())
1458 return BG_JOIN_ERR_ALL_QUEUES_USED
;
1460 return BG_JOIN_ERR_OK
;
1463 //===================================================
1464 //============== Roll ===============================
1465 //===================================================
1467 void Roll::targetObjectBuildLink()
1469 // called from link()
1470 getTarget()->addLootValidatorRef(this);
1473 void Group::SetDungeonDifficulty(Difficulty difficulty
)
1475 m_dungeonDifficulty
= difficulty
;
1477 CharacterDatabase
.PExecute("UPDATE groups SET difficulty = %u WHERE leaderGuid ='%u'", m_dungeonDifficulty
, GUID_LOPART(m_leaderGuid
));
1479 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1481 Player
*player
= itr
->getSource();
1482 if(!player
->GetSession() || player
->getLevel() < LEVELREQUIREMENT_HEROIC
)
1484 player
->SetDungeonDifficulty(difficulty
);
1485 player
->SendDungeonDifficulty(true);
1489 void Group::SetRaidDifficulty(Difficulty difficulty
)
1491 m_raidDifficulty
= difficulty
;
1493 CharacterDatabase
.PExecute("UPDATE groups SET raiddifficulty = %u WHERE leaderGuid ='%u'", m_raidDifficulty
, GUID_LOPART(m_leaderGuid
));
1495 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1497 Player
*player
= itr
->getSource();
1498 if(!player
->GetSession() || player
->getLevel() < LEVELREQUIREMENT_HEROIC
)
1500 player
->SetRaidDifficulty(difficulty
);
1501 player
->SendRaidDifficulty(true);
1505 bool Group::InCombatToInstance(uint32 instanceId
)
1507 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1509 Player
*pPlayer
= itr
->getSource();
1510 if(pPlayer
->getAttackers().size() && pPlayer
->GetInstanceId() == instanceId
)
1516 void Group::ResetInstances(uint8 method
, bool isRaid
, Player
* SendMsgTo
)
1521 // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_DISBAND
1523 // we assume that when the difficulty changes, all instances that can be reset will be
1524 Difficulty diff
= GetDifficulty(isRaid
);
1526 for(BoundInstancesMap::iterator itr
= m_boundInstances
[diff
].begin(); itr
!= m_boundInstances
[diff
].end();)
1528 InstanceSave
*p
= itr
->second
.save
;
1529 const MapEntry
*entry
= sMapStore
.LookupEntry(itr
->first
);
1530 if(!entry
|| entry
->IsRaid() != isRaid
|| !p
->CanReset() && method
!= INSTANCE_RESET_GROUP_DISBAND
)
1536 if(method
== INSTANCE_RESET_ALL
)
1538 // the "reset all instances" method can only reset normal maps
1539 if (entry
->map_type
== MAP_RAID
|| diff
== DUNGEON_DIFFICULTY_HEROIC
)
1546 bool isEmpty
= true;
1547 // if the map is loaded, reset it
1548 Map
*map
= MapManager::Instance().FindMap(p
->GetMapId(), p
->GetInstanceId());
1549 if(map
&& map
->IsDungeon() && !(method
== INSTANCE_RESET_GROUP_DISBAND
&& !p
->CanReset()))
1550 isEmpty
= ((InstanceMap
*)map
)->Reset(method
);
1554 if(isEmpty
) SendMsgTo
->SendResetInstanceSuccess(p
->GetMapId());
1555 else SendMsgTo
->SendResetInstanceFailed(0, p
->GetMapId());
1558 if(isEmpty
|| method
== INSTANCE_RESET_GROUP_DISBAND
|| method
== INSTANCE_RESET_CHANGE_DIFFICULTY
)
1560 // do not reset the instance, just unbind if others are permanently bound to it
1561 if(p
->CanReset()) p
->DeleteFromDB();
1562 else CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p
->GetInstanceId());
1563 // i don't know for sure if hash_map iterators
1564 m_boundInstances
[diff
].erase(itr
);
1565 itr
= m_boundInstances
[diff
].begin();
1566 // this unloads the instance save unless online players are bound to it
1567 // (eg. permanent binds or GM solo binds)
1568 p
->RemoveGroup(this);
1575 InstanceGroupBind
* Group::GetBoundInstance(Player
* player
)
1577 uint32 mapid
= player
->GetMapId();
1578 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(mapid
);
1582 Difficulty difficulty
= player
->GetDifficulty(mapEntry
->IsRaid());
1584 // some instances only have one difficulty
1585 MapDifficulty
const* mapDiff
= GetMapDifficultyData(mapid
,difficulty
);
1587 difficulty
= DUNGEON_DIFFICULTY_NORMAL
;
1589 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
1590 if(itr
!= m_boundInstances
[difficulty
].end())
1591 return &itr
->second
;
1596 InstanceGroupBind
* Group::GetBoundInstance(Map
* aMap
)
1598 // Currently spawn numbering not different from map difficulty
1599 Difficulty difficulty
= Difficulty(aMap
->GetSpawnMode());
1601 // some instances only have one difficulty
1602 MapDifficulty
const* mapDiff
= GetMapDifficultyData(aMap
->GetId(),difficulty
);
1606 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(aMap
->GetId());
1607 if(itr
!= m_boundInstances
[difficulty
].end())
1608 return &itr
->second
;
1613 InstanceGroupBind
* Group::BindToInstance(InstanceSave
*save
, bool permanent
, bool load
)
1615 if(save
&& !isBGGroup())
1617 InstanceGroupBind
& bind
= m_boundInstances
[save
->GetDifficulty()][save
->GetMapId()];
1620 // when a boss is killed or when copying the players's binds to the group
1621 if(permanent
!= bind
.perm
|| save
!= bind
.save
)
1622 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());
1625 if(!load
) CharacterDatabase
.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", GUID_LOPART(GetLeaderGUID()), save
->GetInstanceId(), permanent
);
1627 if(bind
.save
!= save
)
1629 if(bind
.save
) bind
.save
->RemoveGroup(this);
1630 save
->AddGroup(this);
1634 bind
.perm
= permanent
;
1635 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());
1642 void Group::UnbindInstance(uint32 mapid
, uint8 difficulty
, bool unload
)
1644 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
1645 if(itr
!= m_boundInstances
[difficulty
].end())
1647 if(!unload
) CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", GUID_LOPART(GetLeaderGUID()), itr
->second
.save
->GetInstanceId());
1648 itr
->second
.save
->RemoveGroup(this); // save can become invalid
1649 m_boundInstances
[difficulty
].erase(itr
);
1653 void Group::_homebindIfInstance(Player
*player
)
1655 if(player
&& !player
->isGameMaster() && sMapStore
.LookupEntry(player
->GetMapId())->IsDungeon())
1657 // leaving the group in an instance, the homebind timer is started
1658 // unless the player is permanently saved to the instance
1659 InstanceSave
*save
= sInstanceSaveManager
.GetInstanceSave(player
->GetInstanceId());
1660 InstancePlayerBind
*playerBind
= save
? player
->GetBoundInstance(save
->GetMapId(), save
->GetDifficulty()) : NULL
;
1661 if(!playerBind
|| !playerBind
->perm
)
1662 player
->m_InstanceValid
= false;