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"
37 #define LOOT_ROLL_TIMEOUT (1*MINUTE*IN_MILLISECONDS)
39 Group::Group() : m_Id(0), m_leaderGuid(0), m_mainTank(0), m_mainAssistant(0), m_groupType(GROUPTYPE_NORMAL
),
40 m_dungeonDifficulty(REGULAR_DIFFICULTY
), m_raidDifficulty(REGULAR_DIFFICULTY
),
41 m_bgGroup(NULL
), m_lootMethod(FREE_FOR_ALL
), m_looterGuid(0), m_lootThreshold(ITEM_QUALITY_UNCOMMON
),
42 m_subGroupsCounts(NULL
)
44 for (int i
= 0; i
< TARGET_ICON_COUNT
; ++i
)
52 DEBUG_LOG("Group::~Group: battleground group being deleted.");
53 if(m_bgGroup
->GetBgRaid(ALLIANCE
) == this)
54 m_bgGroup
->SetBgRaid(ALLIANCE
, NULL
);
55 else if(m_bgGroup
->GetBgRaid(HORDE
) == this)
56 m_bgGroup
->SetBgRaid(HORDE
, NULL
);
58 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_BGRAID
: 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 m_Id
= sObjectMgr
.GenerateGroupId();
101 Player
*leader
= sObjectMgr
.GetPlayer(guid
);
104 m_dungeonDifficulty
= leader
->GetDungeonDifficulty();
105 m_raidDifficulty
= leader
->GetRaidDifficulty();
108 Player::ConvertInstancesToGroup(leader
, this, guid
);
110 // store group in database
111 CharacterDatabase
.BeginTransaction();
112 CharacterDatabase
.PExecute("DELETE FROM groups WHERE groupId ='%u'", m_Id
);
113 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE groupId ='%u'", m_Id
);
114 CharacterDatabase
.PExecute("INSERT INTO groups (groupId,leaderGuid,mainTank,mainAssistant,lootMethod,looterGuid,lootThreshold,icon1,icon2,icon3,icon4,icon5,icon6,icon7,icon8,groupType,difficulty,raiddifficulty) "
115 "VALUES ('%u','%u','%u','%u','%u','%u','%u','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','" UI64FMTD
"','%u','%u','%u')",
116 m_Id
, GUID_LOPART(m_leaderGuid
), GUID_LOPART(m_mainTank
), GUID_LOPART(m_mainAssistant
), uint32(m_lootMethod
),
117 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], uint8(m_groupType
), uint32(m_dungeonDifficulty
), uint32(m_raidDifficulty
));
120 if(!AddMember(guid
, name
))
124 CharacterDatabase
.CommitTransaction();
129 bool Group::LoadGroupFromDB(Field
* fields
)
131 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
132 // result = CharacterDatabase.Query("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raiddifficulty, leaderGuid, groupId FROM groups");
134 m_Id
= fields
[17].GetUInt32();
135 m_leaderGuid
= MAKE_NEW_GUID(fields
[16].GetUInt32(),0,HIGHGUID_PLAYER
);
137 // group leader not exist
138 if(!sObjectMgr
.GetPlayerNameByGUID(m_leaderGuid
, m_leaderName
))
141 m_groupType
= GroupType(fields
[13].GetUInt8());
143 if (m_groupType
& GROUPTYPE_RAID
)
144 _initRaidSubGroupsCounter();
146 uint32 diff
= fields
[14].GetUInt8();
147 if (diff
>= MAX_DUNGEON_DIFFICULTY
)
148 diff
= DUNGEON_DIFFICULTY_NORMAL
;
149 m_dungeonDifficulty
= Difficulty(diff
);
151 uint32 r_diff
= fields
[15].GetUInt8();
152 if (r_diff
>= MAX_RAID_DIFFICULTY
)
153 r_diff
= RAID_DIFFICULTY_10MAN_NORMAL
;
154 m_raidDifficulty
= Difficulty(r_diff
);
156 m_mainTank
= fields
[0].GetUInt64();
157 m_mainAssistant
= fields
[1].GetUInt64();
158 m_lootMethod
= (LootMethod
)fields
[2].GetUInt8();
159 m_looterGuid
= MAKE_NEW_GUID(fields
[3].GetUInt32(), 0, HIGHGUID_PLAYER
);
160 m_lootThreshold
= (ItemQualities
)fields
[4].GetUInt16();
162 for(int i
= 0; i
< TARGET_ICON_COUNT
; ++i
)
163 m_targetIcons
[i
] = fields
[5+i
].GetUInt64();
168 bool Group::LoadMemberFromDB(uint32 guidLow
, uint8 subgroup
, bool assistant
)
171 member
.guid
= MAKE_NEW_GUID(guidLow
, 0, HIGHGUID_PLAYER
);
173 // skip non-existed member
174 if(!sObjectMgr
.GetPlayerNameByGUID(member
.guid
, member
.name
))
177 member
.group
= subgroup
;
178 member
.assistant
= assistant
;
179 m_memberSlots
.push_back(member
);
181 SubGroupCounterIncrease(subgroup
);
186 void Group::ConvertToRaid()
188 m_groupType
= GroupType(m_groupType
| GROUPTYPE_RAID
);
190 _initRaidSubGroupsCounter();
193 CharacterDatabase
.PExecute("UPDATE groups SET groupType = %u WHERE groupId='%u'", uint8(m_groupType
), m_Id
);
196 // update quest related GO states (quest activity dependent from raid membership)
197 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
198 if(Player
* player
= sObjectMgr
.GetPlayer(citr
->guid
))
199 player
->UpdateForQuestWorldObjects();
202 bool Group::AddInvite(Player
*player
)
204 if( !player
|| player
->GetGroupInvite() )
206 Group
* group
= player
->GetGroup();
207 if( group
&& group
->isBGGroup() )
208 group
= player
->GetOriginalGroup();
212 RemoveInvite(player
);
214 m_invitees
.insert(player
);
216 player
->SetGroupInvite(this);
221 bool Group::AddLeaderInvite(Player
*player
)
223 if(!AddInvite(player
))
226 m_leaderGuid
= player
->GetGUID();
227 m_leaderName
= player
->GetName();
231 uint32
Group::RemoveInvite(Player
*player
)
233 m_invitees
.erase(player
);
235 player
->SetGroupInvite(NULL
);
236 return GetMembersCount();
239 void Group::RemoveAllInvites()
241 for(InvitesList::iterator itr
= m_invitees
.begin(); itr
!=m_invitees
.end(); ++itr
)
242 (*itr
)->SetGroupInvite(NULL
);
247 Player
* Group::GetInvited(const uint64
& guid
) const
249 for(InvitesList::const_iterator itr
= m_invitees
.begin(); itr
!= m_invitees
.end(); ++itr
)
251 if((*itr
)->GetGUID() == guid
)
257 Player
* Group::GetInvited(const std::string
& name
) const
259 for(InvitesList::const_iterator itr
= m_invitees
.begin(); itr
!= m_invitees
.end(); ++itr
)
261 if((*itr
)->GetName() == name
)
267 bool Group::AddMember(const uint64
&guid
, const char* name
)
269 if(!_addMember(guid
, name
))
274 Player
*player
= sObjectMgr
.GetPlayer(guid
);
277 if(!IsLeader(player
->GetGUID()) && !isBGGroup())
279 // reset the new member's instances, unless he is currently in one of them
280 // including raid/heroic instances that they are not permanently bound to!
281 player
->ResetInstances(INSTANCE_RESET_GROUP_JOIN
,false);
282 player
->ResetInstances(INSTANCE_RESET_GROUP_JOIN
,true);
284 if (player
->getLevel() >= LEVELREQUIREMENT_HEROIC
)
286 if (player
->GetDungeonDifficulty() != GetDungeonDifficulty())
288 player
->SetDungeonDifficulty(GetDungeonDifficulty());
289 player
->SendDungeonDifficulty(true);
291 if (player
->GetRaidDifficulty() != GetRaidDifficulty())
293 player
->SetRaidDifficulty(GetRaidDifficulty());
294 player
->SendRaidDifficulty(true);
298 player
->SetGroupUpdateFlag(GROUP_UPDATE_FULL
);
299 UpdatePlayerOutOfRange(player
);
301 // quest related GO state dependent from raid membership
303 player
->UpdateForQuestWorldObjects();
309 uint32
Group::RemoveMember(const uint64
&guid
, const uint8
&method
)
311 // remove member and change leader (if need) only if strong more 2 members _before_ member remove
312 if(GetMembersCount() > uint32(isBGGroup() ? 1 : 2)) // in BG group case allow 1 members group
314 bool leaderChanged
= _removeMember(guid
);
316 if(Player
*player
= sObjectMgr
.GetPlayer( guid
))
318 // quest related GO state dependent from raid membership
320 player
->UpdateForQuestWorldObjects();
326 data
.Initialize( SMSG_GROUP_UNINVITE
, 0 );
327 player
->GetSession()->SendPacket( &data
);
330 //we already removed player from group and in player->GetGroup() is his original group!
331 if( Group
* group
= player
->GetGroup() )
337 data
.Initialize(SMSG_GROUP_LIST
, 1+1+1+1+8+4+4+8);
338 data
<< uint8(0x10) << uint8(0) << uint8(0) << uint8(0);
339 data
<< uint64(0) << uint32(0) << uint32(0) << uint64(0);
340 player
->GetSession()->SendPacket(&data
);
343 _homebindIfInstance(player
);
348 WorldPacket
data(SMSG_GROUP_SET_LEADER
, (m_memberSlots
.front().name
.size()+1));
349 data
<< m_memberSlots
.front().name
;
350 BroadcastPacket(&data
, true);
355 // if group before remove <= 2 disband it
359 return m_memberSlots
.size();
362 void Group::ChangeLeader(const uint64
&guid
)
364 member_citerator slot
= _getMemberCSlot(guid
);
366 if(slot
== m_memberSlots
.end())
371 WorldPacket
data(SMSG_GROUP_SET_LEADER
, slot
->name
.size()+1);
373 BroadcastPacket(&data
, true);
377 void Group::Disband(bool hideDestroy
)
381 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
383 player
= sObjectMgr
.GetPlayer(citr
->guid
);
387 //we cannot call _removeMember because it would invalidate member iterator
388 //if we are removing player from battleground raid
390 player
->RemoveFromBattleGroundRaid();
393 //we can remove player who is in battleground from his original group
394 if( player
->GetOriginalGroup() == this )
395 player
->SetOriginalGroup(NULL
);
397 player
->SetGroup(NULL
);
400 // quest related GO state dependent from raid membership
402 player
->UpdateForQuestWorldObjects();
404 if(!player
->GetSession())
410 data
.Initialize(SMSG_GROUP_DESTROYED
, 0);
411 player
->GetSession()->SendPacket(&data
);
414 //we already removed player from group and in player->GetGroup() is his original group, send update
415 if( Group
* group
= player
->GetGroup() )
421 data
.Initialize(SMSG_GROUP_LIST
, 1+1+1+1+8+4+4+8);
422 data
<< uint8(0x10) << uint8(0) << uint8(0) << uint8(0);
423 data
<< uint64(0) << uint32(0) << uint32(0) << uint64(0);
424 player
->GetSession()->SendPacket(&data
);
427 _homebindIfInstance(player
);
430 m_memberSlots
.clear();
436 CharacterDatabase
.BeginTransaction();
437 CharacterDatabase
.PExecute("DELETE FROM groups WHERE groupId='%u'", m_Id
);
438 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE groupId='%u'", m_Id
);
439 CharacterDatabase
.CommitTransaction();
440 ResetInstances(INSTANCE_RESET_GROUP_DISBAND
, false, NULL
);
441 ResetInstances(INSTANCE_RESET_GROUP_DISBAND
, true, NULL
);
448 /*********************************************************/
449 /*** LOOT SYSTEM ***/
450 /*********************************************************/
452 void Group::SendLootStartRoll(uint32 CountDown
, uint32 mapid
, const Roll
&r
)
454 WorldPacket
data(SMSG_LOOT_START_ROLL
, (8+4+4+4+4+4+4+1));
455 data
<< r
.lootedTargetGUID
; // creature guid what we're looting
456 data
<< uint32(mapid
); // 3.3.3 mapid
457 data
<< uint32(r
.itemSlot
); // item slot in loot
458 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
459 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
460 data
<< uint32(r
.itemRandomPropId
); // item random property ID
461 data
<< uint32(r
.itemCount
); // items in stack
462 data
<< uint32(CountDown
); // the countdown time to choose "need" or "greed"
463 data
<< uint8(ALL_ROLL_VOTE_MASK
); // roll type mask, allowed choises
465 for (Roll::PlayerVote::const_iterator itr
= r
.playerVote
.begin(); itr
!= r
.playerVote
.end(); ++itr
)
467 Player
*p
= sObjectMgr
.GetPlayer(itr
->first
);
468 if(!p
|| !p
->GetSession())
471 if(itr
->second
!= ROLL_NOT_VALID
)
472 p
->GetSession()->SendPacket( &data
);
476 void Group::SendLootRoll(ObjectGuid
const& targetGuid
, uint8 rollNumber
, uint8 rollType
, const Roll
&r
)
478 WorldPacket
data(SMSG_LOOT_ROLL
, (8+4+8+4+4+4+1+1+1));
479 data
<< r
.lootedTargetGUID
; // creature guid what we're looting
480 data
<< uint32(r
.itemSlot
); // unknown, maybe amount of players, or item slot in loot
482 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
483 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
484 data
<< uint32(r
.itemRandomPropId
); // Item random property ID
485 data
<< uint8(rollNumber
); // 0: "Need for: [item name]" > 127: "you passed on: [item name]" Roll number
486 data
<< uint8(rollType
); // 0: "Need for: [item name]" 0: "You have selected need for [item name] 1: need roll 2: greed roll
487 data
<< uint8(0); // auto pass on loot
489 for( Roll::PlayerVote::const_iterator itr
= r
.playerVote
.begin(); itr
!= r
.playerVote
.end(); ++itr
)
491 Player
*p
= sObjectMgr
.GetPlayer(itr
->first
);
492 if(!p
|| !p
->GetSession())
495 if(itr
->second
!= ROLL_NOT_VALID
)
496 p
->GetSession()->SendPacket( &data
);
500 void Group::SendLootRollWon(ObjectGuid
const& targetGuid
, uint8 rollNumber
, RollVote rollType
, const Roll
&r
)
502 WorldPacket
data(SMSG_LOOT_ROLL_WON
, (8+4+4+4+4+8+1+1));
503 data
<< r
.lootedTargetGUID
; // creature guid what we're looting
504 data
<< uint32(r
.itemSlot
); // item slot in loot
505 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
506 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
507 data
<< uint32(r
.itemRandomPropId
); // Item random property
508 data
<< targetGuid
; // guid of the player who won.
509 data
<< uint8(rollNumber
); // rollnumber related to SMSG_LOOT_ROLL
510 data
<< uint8(rollType
); // Rolltype related to SMSG_LOOT_ROLL
512 for( Roll::PlayerVote::const_iterator itr
= r
.playerVote
.begin(); itr
!= r
.playerVote
.end(); ++itr
)
514 Player
*p
= sObjectMgr
.GetPlayer(itr
->first
);
515 if(!p
|| !p
->GetSession())
518 if(itr
->second
!= ROLL_NOT_VALID
)
519 p
->GetSession()->SendPacket( &data
);
523 void Group::SendLootAllPassed(Roll
const& r
)
525 WorldPacket
data(SMSG_LOOT_ALL_PASSED
, (8+4+4+4+4));
526 data
<< r
.lootedTargetGUID
; // creature guid what we're looting
527 data
<< uint32(r
.itemSlot
); // item slot in loot
528 data
<< uint32(r
.itemid
); // The itemEntryId for the item that shall be rolled for
529 data
<< uint32(r
.itemRandomPropId
); // Item random property ID
530 data
<< uint32(r
.itemRandomSuffix
); // Item random suffix ID
532 for( Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
534 Player
*p
= sObjectMgr
.GetPlayer(itr
->first
);
535 if(!p
|| !p
->GetSession())
538 if(itr
->second
!= ROLL_NOT_VALID
)
539 p
->GetSession()->SendPacket( &data
);
543 void Group::GroupLoot(Creature
*creature
, Loot
*loot
)
545 for(uint8 itemSlot
= 0; itemSlot
< loot
->items
.size(); ++itemSlot
)
547 LootItem
& lootItem
= loot
->items
[itemSlot
];
548 ItemPrototype
const *itemProto
= ObjectMgr::GetItemPrototype(lootItem
.itemid
);
551 DEBUG_LOG("Group::GroupLoot: missing item prototype for item with id: %d", lootItem
.itemid
);
555 //roll for over-threshold item if it's one-player loot
556 if (itemProto
->Quality
>= uint32(m_lootThreshold
) && !lootItem
.freeforall
)
557 StartLootRool(creature
,loot
,itemSlot
,false);
559 lootItem
.is_underthreshold
= 1;
563 void Group::NeedBeforeGreed(Creature
*creature
, Loot
*loot
)
565 for(uint8 itemSlot
= 0; itemSlot
< loot
->items
.size(); ++itemSlot
)
567 LootItem
& lootItem
= loot
->items
[itemSlot
];
568 ItemPrototype
const *itemProto
= ObjectMgr::GetItemPrototype(lootItem
.itemid
);
571 DEBUG_LOG("Group::NeedBeforeGreed: missing item prototype for item with id: %d", lootItem
.itemid
);
575 //only roll for one-player items, not for ones everyone can get
576 if (itemProto
->Quality
>= uint32(m_lootThreshold
) && !lootItem
.freeforall
)
577 StartLootRool(creature
, loot
, itemSlot
, true);
579 lootItem
.is_underthreshold
= 1;
583 void Group::MasterLoot(Creature
*creature
, Loot
* loot
)
585 for (LootItemList::iterator i
=loot
->items
.begin(); i
!= loot
->items
.end(); ++i
)
587 ItemPrototype
const *item
= ObjectMgr::GetItemPrototype(i
->itemid
);
590 if (item
->Quality
< uint32(m_lootThreshold
))
591 i
->is_underthreshold
= 1;
594 uint32 real_count
= 0;
596 WorldPacket
data(SMSG_LOOT_MASTER_LIST
, 330);
597 data
<< uint8(GetMembersCount());
599 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
601 Player
*looter
= itr
->getSource();
602 if (!looter
->IsInWorld())
605 if (looter
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
607 data
<< uint64(looter
->GetGUID());
612 data
.put
<uint8
>(0, real_count
);
614 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
616 Player
*looter
= itr
->getSource();
617 if (looter
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
618 looter
->GetSession()->SendPacket(&data
);
622 void Group::CountRollVote(ObjectGuid
const& playerGUID
, ObjectGuid
const& lootedTarget
, uint32 itemSlot
, RollVote choise
)
624 Rolls::iterator rollI
= RollId
.begin();
625 for (; rollI
!= RollId
.end(); ++rollI
)
626 if ((*rollI
)->isValid() && (*rollI
)->lootedTargetGUID
== lootedTarget
&& (*rollI
)->itemSlot
== itemSlot
)
629 if (rollI
== RollId
.end())
632 CountRollVote(playerGUID
, rollI
, choise
);
635 bool Group::CountRollVote(ObjectGuid
const& playerGUID
, Rolls::iterator
& rollI
, RollVote choise
)
639 Roll::PlayerVote::iterator itr
= roll
->playerVote
.find(playerGUID
.GetRawValue());
640 // this condition means that player joins to the party after roll begins
641 if (itr
== roll
->playerVote
.end())
642 return true; // result used for need iterator ++, so avoid for end of list
645 if (roll
->getLoot()->items
.empty())
650 case ROLL_PASS
: // Player choose pass
652 SendLootRoll(playerGUID
, 128, 128, *roll
);
654 itr
->second
= ROLL_PASS
;
657 case ROLL_NEED
: // player choose Need
659 SendLootRoll(playerGUID
, 0, 0, *roll
);
661 itr
->second
= ROLL_NEED
;
664 case ROLL_GREED
: // player choose Greed
666 SendLootRoll(playerGUID
, 128, ROLL_GREED
, *roll
);
668 itr
->second
= ROLL_GREED
;
671 case ROLL_DISENCHANT
: // player choose Disenchant
673 SendLootRoll(playerGUID
, 128, ROLL_DISENCHANT
, *roll
);
675 itr
->second
= ROLL_DISENCHANT
;
678 default: // Roll removed case
682 if (roll
->totalPass
+ roll
->totalNeed
+ roll
->totalGreed
>= roll
->totalPlayersRolling
)
691 void Group::StartLootRool(Creature
* lootTarget
, Loot
* loot
, uint8 itemSlot
, bool skipIfCanNotUse
)
693 if (itemSlot
>= loot
->items
.size())
696 LootItem
const& lootItem
= loot
->items
[itemSlot
];
698 ItemPrototype
const* item
= ObjectMgr::GetItemPrototype(lootItem
.itemid
);
700 Roll
* r
= new Roll(lootTarget
->GetGUID(), lootItem
);
702 //a vector is filled with only near party members
703 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
705 Player
*playerToRoll
= itr
->getSource();
706 if(!playerToRoll
|| !playerToRoll
->GetSession())
709 if ((!skipIfCanNotUse
|| playerToRoll
->CanUseItem(item
)) && lootItem
.AllowedForPlayer(playerToRoll
) )
711 if (playerToRoll
->IsWithinDist(lootTarget
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
713 r
->playerVote
[playerToRoll
->GetGUID()] = ROLL_NOT_EMITED_YET
;
714 ++r
->totalPlayersRolling
;
719 if (r
->totalPlayersRolling
> 0) // has looters
722 r
->itemSlot
= itemSlot
;
724 if (r
->totalPlayersRolling
== 1) // single looter
725 r
->playerVote
.begin()->second
= ROLL_NEED
;
728 SendLootStartRoll(LOOT_ROLL_TIMEOUT
, lootTarget
->GetMapId(), *r
);
729 loot
->items
[itemSlot
].is_blocked
= true;
730 lootTarget
->StartGroupLoot(this,LOOT_ROLL_TIMEOUT
);
739 // called when roll timer expires
740 void Group::EndRoll()
742 while(!RollId
.empty())
744 //need more testing here, if rolls disappear
745 Rolls::iterator itr
= RollId
.begin();
746 CountTheRoll(itr
); //i don't have to edit player votes, who didn't vote ... he will pass
750 void Group::CountTheRoll(Rolls::iterator
& rollI
)
753 if(!roll
->isValid()) // is loot already deleted ?
755 rollI
= RollId
.erase(rollI
);
761 if (roll
->totalNeed
> 0)
763 if(!roll
->playerVote
.empty())
766 ObjectGuid maxguid
= (*roll
->playerVote
.begin()).first
;
769 for( Roll::PlayerVote::const_iterator itr
= roll
->playerVote
.begin(); itr
!= roll
->playerVote
.end(); ++itr
)
771 if (itr
->second
!= ROLL_NEED
)
774 uint8 randomN
= urand(1, 99);
775 SendLootRoll(itr
->first
, randomN
, ROLL_NEED
, *roll
);
776 if (maxresul
< randomN
)
778 maxguid
= itr
->first
;
782 SendLootRollWon(maxguid
, maxresul
, ROLL_NEED
, *roll
);
783 player
= sObjectMgr
.GetPlayer(maxguid
);
785 if(player
&& player
->GetSession())
787 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT
, roll
->itemid
, maxresul
);
789 ItemPosCountVec dest
;
790 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
791 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, roll
->itemid
, item
->count
);
792 if ( msg
== EQUIP_ERR_OK
)
794 item
->is_looted
= true;
795 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
796 --roll
->getLoot()->unlootedCount
;
797 player
->StoreNewItem( dest
, roll
->itemid
, true, item
->randomPropertyId
);
801 item
->is_blocked
= false;
802 player
->SendEquipError( msg
, NULL
, NULL
, roll
->itemid
);
807 else if (roll
->totalGreed
> 0)
809 if(!roll
->playerVote
.empty())
812 uint64 maxguid
= (*roll
->playerVote
.begin()).first
;
814 RollVote rollvote
= ROLL_PASS
; //Fixed: Using uninitialized memory 'rollvote'
816 Roll::PlayerVote::iterator itr
;
817 for (itr
= roll
->playerVote
.begin(); itr
!= roll
->playerVote
.end(); ++itr
)
819 if (itr
->second
!= ROLL_GREED
&& itr
->second
!= ROLL_DISENCHANT
)
822 uint8 randomN
= urand(1, 99);
823 SendLootRoll(itr
->first
, randomN
, itr
->second
, *roll
);
824 if (maxresul
< randomN
)
826 maxguid
= itr
->first
;
828 rollvote
= itr
->second
;
831 SendLootRollWon(maxguid
, maxresul
, rollvote
, *roll
);
832 player
= sObjectMgr
.GetPlayer(maxguid
);
834 if(player
&& player
->GetSession())
836 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT
, roll
->itemid
, maxresul
);
838 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
840 if(rollvote
== ROLL_GREED
)
842 ItemPosCountVec dest
;
843 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, roll
->itemid
, item
->count
);
844 if ( msg
== EQUIP_ERR_OK
)
846 item
->is_looted
= true;
847 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
848 --roll
->getLoot()->unlootedCount
;
849 player
->StoreNewItem( dest
, roll
->itemid
, true, item
->randomPropertyId
);
853 item
->is_blocked
= false;
854 player
->SendEquipError( msg
, NULL
, NULL
, roll
->itemid
);
857 else if(rollvote
== ROLL_DISENCHANT
)
859 item
->is_looted
= true;
860 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
861 --roll
->getLoot()->unlootedCount
;
863 ItemPrototype
const *pProto
= ObjectMgr::GetItemPrototype(roll
->itemid
);
864 player
->AutoStoreLoot(pProto
->DisenchantID
, LootTemplates_Disenchant
, true);
871 SendLootAllPassed(*roll
);
872 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
873 if(item
) item
->is_blocked
= false;
875 rollI
= RollId
.erase(rollI
);
879 void Group::SetTargetIcon(uint8 id
, uint64 whoGuid
, uint64 targetGuid
)
881 if(id
>= TARGET_ICON_COUNT
)
885 if( targetGuid
!= 0 )
886 for(int i
= 0; i
< TARGET_ICON_COUNT
; ++i
)
887 if( m_targetIcons
[i
] == targetGuid
)
888 SetTargetIcon(i
, 0, 0);
890 m_targetIcons
[id
] = targetGuid
;
892 WorldPacket
data(MSG_RAID_TARGET_UPDATE
, (1+8+1+8));
893 data
<< uint8(0); // set targets
894 data
<< uint64(whoGuid
);
896 data
<< uint64(targetGuid
);
897 BroadcastPacket(&data
, true);
900 static void GetDataForXPAtKill_helper(Player
* player
, Unit
const* victim
, uint32
& sum_level
, Player
* & member_with_max_level
, Player
* & not_gray_member_with_max_level
)
902 sum_level
+= player
->getLevel();
903 if(!member_with_max_level
|| member_with_max_level
->getLevel() < player
->getLevel())
904 member_with_max_level
= player
;
906 uint32 gray_level
= MaNGOS::XP::GetGrayLevel(player
->getLevel());
907 if( victim
->getLevel() > gray_level
&& (!not_gray_member_with_max_level
908 || not_gray_member_with_max_level
->getLevel() < player
->getLevel()))
909 not_gray_member_with_max_level
= player
;
912 void Group::GetDataForXPAtKill(Unit
const* victim
, uint32
& count
,uint32
& sum_level
, Player
* & member_with_max_level
, Player
* & not_gray_member_with_max_level
, Player
* additional
)
914 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
916 Player
* member
= itr
->getSource();
917 if (!member
|| !member
->isAlive()) // only for alive
920 // will proccesed later
921 if (member
== additional
)
924 if (!member
->IsAtGroupRewardDistance(victim
)) // at req. distance
928 GetDataForXPAtKill_helper(member
,victim
,sum_level
,member_with_max_level
,not_gray_member_with_max_level
);
933 if (additional
->IsAtGroupRewardDistance(victim
)) // at req. distance
936 GetDataForXPAtKill_helper(additional
,victim
,sum_level
,member_with_max_level
,not_gray_member_with_max_level
);
941 void Group::SendTargetIconList(WorldSession
*session
)
946 WorldPacket
data(MSG_RAID_TARGET_UPDATE
, (1+TARGET_ICON_COUNT
*9));
947 data
<< uint8(1); // list targets
949 for(int i
= 0; i
< TARGET_ICON_COUNT
; ++i
)
951 if(m_targetIcons
[i
] == 0)
955 data
<< uint64(m_targetIcons
[i
]);
958 session
->SendPacket(&data
);
961 void Group::SendUpdate()
965 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
967 player
= sObjectMgr
.GetPlayer(citr
->guid
);
968 if(!player
|| !player
->GetSession() || player
->GetGroup() != this )
971 WorldPacket
data(SMSG_GROUP_LIST
, (1+1+1+1+8+4+GetMembersCount()*20));
972 data
<< uint8(m_groupType
); // group type (flags in 3.3)
973 data
<< uint8(citr
->group
); // groupid
974 data
<< uint8(GetFlags(*citr
)); // group flags
975 data
<< uint8(isBGGroup() ? 1 : 0); // 2.0.x, isBattleGroundGroup?
976 if(m_groupType
& GROUPTYPE_LFD
)
981 data
<< uint64(0x50000000FFFFFFFELL
); // related to voice chat?
982 data
<< uint32(0); // 3.3, this value increments every time SMSG_GROUP_LIST is sent
983 data
<< uint32(GetMembersCount()-1);
984 for(member_citerator citr2
= m_memberSlots
.begin(); citr2
!= m_memberSlots
.end(); ++citr2
)
986 if(citr
->guid
== citr2
->guid
)
988 Player
* member
= sObjectMgr
.GetPlayer(citr2
->guid
);
989 uint8 onlineState
= (member
) ? MEMBER_STATUS_ONLINE
: MEMBER_STATUS_OFFLINE
;
990 onlineState
= onlineState
| ((isBGGroup()) ? MEMBER_STATUS_PVP
: 0);
993 data
<< uint64(citr2
->guid
);
994 data
<< uint8(onlineState
); // online-state
995 data
<< uint8(citr2
->group
); // groupid
996 data
<< uint8(GetFlags(*citr2
)); // group flags
997 data
<< uint8(0); // 3.3, role?
1000 data
<< uint64(m_leaderGuid
); // leader guid
1001 if(GetMembersCount()-1)
1003 data
<< uint8(m_lootMethod
); // loot method
1004 data
<< uint64(m_looterGuid
); // looter guid
1005 data
<< uint8(m_lootThreshold
); // loot threshold
1006 data
<< uint8(m_dungeonDifficulty
); // Dungeon Difficulty
1007 data
<< uint8(m_raidDifficulty
); // Raid Difficulty
1008 data
<< uint8(0); // 3.3, dynamic difficulty?
1010 player
->GetSession()->SendPacket( &data
);
1014 void Group::UpdatePlayerOutOfRange(Player
* pPlayer
)
1016 if(!pPlayer
|| !pPlayer
->IsInWorld())
1019 if (pPlayer
->GetGroupUpdateFlag() == GROUP_UPDATE_FLAG_NONE
)
1023 pPlayer
->GetSession()->BuildPartyMemberStatsChangedPacket(pPlayer
, &data
);
1025 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1026 if (Player
*player
= itr
->getSource())
1027 if (player
!= pPlayer
&& !player
->HaveAtClient(pPlayer
))
1028 player
->GetSession()->SendPacket(&data
);
1031 void Group::BroadcastPacket(WorldPacket
*packet
, bool ignorePlayersInBGRaid
, int group
, uint64 ignore
)
1033 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1035 Player
*pl
= itr
->getSource();
1036 if(!pl
|| (ignore
!= 0 && pl
->GetGUID() == ignore
) || (ignorePlayersInBGRaid
&& pl
->GetGroup() != this) )
1039 if (pl
->GetSession() && (group
== -1 || itr
->getSubGroup() == group
))
1040 pl
->GetSession()->SendPacket(packet
);
1044 void Group::BroadcastReadyCheck(WorldPacket
*packet
)
1046 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1048 Player
*pl
= itr
->getSource();
1049 if(pl
&& pl
->GetSession())
1050 if(IsLeader(pl
->GetGUID()) || IsAssistant(pl
->GetGUID()))
1051 pl
->GetSession()->SendPacket(packet
);
1055 void Group::OfflineReadyCheck()
1057 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
1059 Player
*pl
= sObjectMgr
.GetPlayer(citr
->guid
);
1060 if (!pl
|| !pl
->GetSession())
1062 WorldPacket
data(MSG_RAID_READY_CHECK_CONFIRM
, 9);
1063 data
<< uint64(citr
->guid
);
1065 BroadcastReadyCheck(&data
);
1070 bool Group::_addMember(const uint64
&guid
, const char* name
, bool isAssistant
)
1072 // get first not-full group
1074 if (m_subGroupsCounts
)
1076 bool groupFound
= false;
1077 for (; groupid
< MAX_RAID_SUBGROUPS
; ++groupid
)
1079 if (m_subGroupsCounts
[groupid
] < MAX_GROUP_SIZE
)
1085 // We are raid group and no one slot is free
1090 return _addMember(guid
, name
, isAssistant
, groupid
);
1093 bool Group::_addMember(const uint64
&guid
, const char* name
, bool isAssistant
, uint8 group
)
1101 Player
*player
= sObjectMgr
.GetPlayer(guid
);
1106 member
.group
= group
;
1107 member
.assistant
= isAssistant
;
1108 m_memberSlots
.push_back(member
);
1110 SubGroupCounterIncrease(group
);
1114 player
->SetGroupInvite(NULL
);
1115 //if player is in group and he is being added to BG raid group, then call SetBattleGroundRaid()
1116 if( player
->GetGroup() && isBGGroup() )
1117 player
->SetBattleGroundRaid(this, group
);
1118 //if player is in bg raid and we are adding him to normal group, then call SetOriginalGroup()
1119 else if ( player
->GetGroup() )
1120 player
->SetOriginalGroup(this, group
);
1121 //if player is not in group, then call set group
1123 player
->SetGroup(this, group
);
1124 // if the same group invites the player back, cancel the homebind timer
1125 InstanceGroupBind
*bind
= GetBoundInstance(player
);
1126 if(bind
&& bind
->save
->GetInstanceId() == player
->GetInstanceId())
1127 player
->m_InstanceValid
= true;
1130 if(!isRaidGroup()) // reset targetIcons for non-raid-groups
1132 for(int i
= 0; i
< TARGET_ICON_COUNT
; ++i
)
1133 m_targetIcons
[i
] = 0;
1138 // insert into group table
1139 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
);
1145 bool Group::_removeMember(const uint64
&guid
)
1147 Player
*player
= sObjectMgr
.GetPlayer(guid
);
1150 //if we are removing player from battleground raid
1152 player
->RemoveFromBattleGroundRaid();
1155 //we can remove player who is in battleground from his original group
1156 if( player
->GetOriginalGroup() == this )
1157 player
->SetOriginalGroup(NULL
);
1159 player
->SetGroup(NULL
);
1165 member_witerator slot
= _getMemberWSlot(guid
);
1166 if (slot
!= m_memberSlots
.end())
1168 SubGroupCounterDecrease(slot
->group
);
1170 m_memberSlots
.erase(slot
);
1174 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE memberGuid='%u'", GUID_LOPART(guid
));
1176 if(m_leaderGuid
== guid
) // leader was removed
1178 if(GetMembersCount() > 0)
1179 _setLeader(m_memberSlots
.front().guid
);
1186 void Group::_setLeader(const uint64
&guid
)
1188 member_citerator slot
= _getMemberCSlot(guid
);
1189 if(slot
== m_memberSlots
.end())
1194 // TODO: set a time limit to have this function run rarely cause it can be slow
1195 CharacterDatabase
.BeginTransaction();
1197 // update the group's bound instances when changing leaders
1199 // remove all permanent binds from the group
1200 // in the DB also remove solo binds that will be replaced with permbinds
1201 // from the new leader
1202 CharacterDatabase
.PExecute(
1203 "DELETE FROM group_instance WHERE leaderguid='%u' AND (permanent = 1 OR "
1204 "instance IN (SELECT instance FROM character_instance WHERE guid = '%u')"
1205 ")", GUID_LOPART(m_leaderGuid
), GUID_LOPART(slot
->guid
)
1208 Player
*player
= sObjectMgr
.GetPlayer(slot
->guid
);
1211 for(uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
1213 for(BoundInstancesMap::iterator itr
= m_boundInstances
[i
].begin(); itr
!= m_boundInstances
[i
].end();)
1215 if(itr
->second
.perm
)
1217 itr
->second
.save
->RemoveGroup(this);
1218 m_boundInstances
[i
].erase(itr
++);
1226 // update the group's solo binds to the new leader
1227 CharacterDatabase
.PExecute("UPDATE group_instance SET leaderGuid='%u' WHERE leaderGuid = '%u'", GUID_LOPART(slot
->guid
), GUID_LOPART(m_leaderGuid
));
1229 // copy the permanent binds from the new leader to the group
1230 // overwriting the solo binds with permanent ones if necessary
1231 // in the DB those have been deleted already
1232 Player::ConvertInstancesToGroup(player
, this, slot
->guid
);
1234 // update the group leader
1235 CharacterDatabase
.PExecute("UPDATE groups SET leaderGuid='%u' WHERE groupId='%u'", GUID_LOPART(slot
->guid
), m_Id
);
1236 CharacterDatabase
.CommitTransaction();
1239 m_leaderGuid
= slot
->guid
;
1240 m_leaderName
= slot
->name
;
1243 void Group::_removeRolls(const uint64
&guid
)
1245 for (Rolls::iterator it
= RollId
.begin(); it
!= RollId
.end(); )
1248 Roll::PlayerVote::iterator itr2
= roll
->playerVote
.find(guid
);
1249 if(itr2
== roll
->playerVote
.end())
1255 if (itr2
->second
== ROLL_GREED
|| itr2
->second
== ROLL_DISENCHANT
)
1257 if (itr2
->second
== ROLL_NEED
)
1259 if (itr2
->second
== ROLL_PASS
)
1261 if (itr2
->second
!= ROLL_NOT_VALID
)
1262 --roll
->totalPlayersRolling
;
1264 roll
->playerVote
.erase(itr2
);
1266 if (!CountRollVote(guid
, it
, ROLL_NOT_EMITED_YET
))
1271 bool Group::_setMembersGroup(const uint64
&guid
, uint8 group
)
1273 member_witerator slot
= _getMemberWSlot(guid
);
1274 if(slot
== m_memberSlots
.end())
1277 slot
->group
= group
;
1279 SubGroupCounterIncrease(group
);
1282 CharacterDatabase
.PExecute("UPDATE group_member SET subgroup='%u' WHERE memberGuid='%u'", group
, GUID_LOPART(guid
));
1287 bool Group::_setAssistantFlag(const uint64
&guid
, const bool &state
)
1289 member_witerator slot
= _getMemberWSlot(guid
);
1290 if(slot
== m_memberSlots
.end())
1293 slot
->assistant
= state
;
1295 CharacterDatabase
.PExecute("UPDATE group_member SET assistant='%u' WHERE memberGuid='%u'", (state
==true)?1:0, GUID_LOPART(guid
));
1299 bool Group::_setMainTank(const uint64
&guid
)
1301 if (m_mainTank
== guid
)
1306 member_citerator slot
= _getMemberCSlot(guid
);
1307 if(slot
== m_memberSlots
.end())
1310 if(m_mainAssistant
== guid
)
1311 _setMainAssistant(0);
1317 CharacterDatabase
.PExecute("UPDATE groups SET mainTank='%u' WHERE groupId='%u'", GUID_LOPART(m_mainTank
), m_Id
);
1322 bool Group::_setMainAssistant(const uint64
&guid
)
1324 if (m_mainAssistant
== guid
)
1329 member_witerator slot
= _getMemberWSlot(guid
);
1330 if(slot
== m_memberSlots
.end())
1333 if(m_mainTank
== guid
)
1337 m_mainAssistant
= guid
;
1340 CharacterDatabase
.PExecute("UPDATE groups SET mainAssistant='%u' WHERE groupId='%u'", GUID_LOPART(m_mainAssistant
), m_Id
);
1345 bool Group::SameSubGroup(Player
const* member1
, Player
const* member2
) const
1347 if(!member1
|| !member2
)
1349 if (member1
->GetGroup() != this || member2
->GetGroup() != this)
1352 return member1
->GetSubGroup() == member2
->GetSubGroup();
1355 // allows setting subgroup for offline members
1356 void Group::ChangeMembersGroup(const uint64
&guid
, uint8 group
)
1361 Player
*player
= sObjectMgr
.GetPlayer(guid
);
1365 uint8 prevSubGroup
= GetMemberGroup(guid
);
1366 if (prevSubGroup
== group
)
1369 if(_setMembersGroup(guid
, group
))
1371 SubGroupCounterDecrease(prevSubGroup
);
1376 // This methods handles itself groupcounter decrease
1377 ChangeMembersGroup(player
, group
);
1380 // only for online members
1381 void Group::ChangeMembersGroup(Player
*player
, uint8 group
)
1383 if (!player
|| !isRaidGroup())
1386 uint8 prevSubGroup
= player
->GetSubGroup();
1387 if (prevSubGroup
== group
)
1390 if (_setMembersGroup(player
->GetGUID(), group
))
1392 if (player
->GetGroup() == this)
1393 player
->GetGroupRef().setSubGroup(group
);
1394 //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
1397 prevSubGroup
= player
->GetOriginalSubGroup();
1398 player
->GetOriginalGroupRef().setSubGroup(group
);
1400 SubGroupCounterDecrease(prevSubGroup
);
1406 void Group::UpdateLooterGuid( Creature
* creature
, bool ifneed
)
1408 switch (GetLootMethod())
1414 // round robin style looting applies for all low
1415 // quality items in each loot method except free for all and master loot
1419 member_citerator guid_itr
= _getMemberCSlot(GetLooterGuid());
1420 if(guid_itr
!= m_memberSlots
.end())
1424 // not update if only update if need and ok
1425 Player
* looter
= ObjectAccessor::FindPlayer(guid_itr
->guid
);
1426 if(looter
&& looter
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
1432 // search next after current
1433 if(guid_itr
!= m_memberSlots
.end())
1435 for(member_citerator itr
= guid_itr
; itr
!= m_memberSlots
.end(); ++itr
)
1437 if(Player
* pl
= ObjectAccessor::FindPlayer(itr
->guid
))
1439 if (pl
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
1441 bool refresh
= pl
->GetLootGUID() == creature
->GetGUID();
1443 //if(refresh) // update loot for new looter
1444 // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1445 SetLooterGuid(pl
->GetGUID());
1447 if(refresh
) // update loot for new looter
1448 pl
->SendLoot(creature
->GetGUID(), LOOT_CORPSE
);
1455 // search from start
1456 for(member_citerator itr
= m_memberSlots
.begin(); itr
!= guid_itr
; ++itr
)
1458 if(Player
* pl
= ObjectAccessor::FindPlayer(itr
->guid
))
1460 if (pl
->IsWithinDist(creature
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
), false))
1462 bool refresh
= pl
->GetLootGUID()==creature
->GetGUID();
1464 //if(refresh) // update loot for new looter
1465 // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1466 SetLooterGuid(pl
->GetGUID());
1468 if(refresh
) // update loot for new looter
1469 pl
->SendLoot(creature
->GetGUID(), LOOT_CORPSE
);
1479 GroupJoinBattlegroundResult
Group::CanJoinBattleGroundQueue(BattleGround
const* bgOrTemplate
, BattleGroundQueueTypeId bgQueueTypeId
, uint32 MinPlayerCount
, uint32 MaxPlayerCount
, bool isRated
, uint32 arenaSlot
)
1481 BattlemasterListEntry
const* bgEntry
= sBattlemasterListStore
.LookupEntry(bgOrTemplate
->GetTypeID());
1483 return ERR_GROUP_JOIN_BATTLEGROUND_FAIL
; // shouldn't happen
1485 // check for min / max count
1486 uint32 memberscount
= GetMembersCount();
1488 // only check for MinPlayerCount since MinPlayerCount == MaxPlayerCount for arenas...
1489 if(bgOrTemplate
->isArena() && memberscount
!= MinPlayerCount
)
1490 return ERR_ARENA_TEAM_PARTY_SIZE
;
1492 if(memberscount
> bgEntry
->maxGroupSize
) // no MinPlayerCount for battlegrounds
1493 return ERR_BATTLEGROUND_NONE
; // ERR_GROUP_JOIN_BATTLEGROUND_TOO_MANY handled on client side
1495 // get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.)
1496 Player
* reference
= GetFirstMember()->getSource();
1497 // no reference found, can't join this way
1499 return ERR_BATTLEGROUND_JOIN_FAILED
;
1501 PvPDifficultyEntry
const* bracketEntry
= GetBattlegroundBracketByLevel(bgOrTemplate
->GetMapId(), reference
->getLevel());
1503 return ERR_BATTLEGROUND_JOIN_FAILED
;
1505 uint32 arenaTeamId
= reference
->GetArenaTeamId(arenaSlot
);
1506 uint32 team
= reference
->GetTeam();
1508 // check every member of the group to be able to join
1509 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1511 Player
*member
= itr
->getSource();
1512 // offline member? don't let join
1514 return ERR_BATTLEGROUND_JOIN_FAILED
;
1515 // don't allow cross-faction join as group
1516 if(member
->GetTeam() != team
)
1517 return ERR_BATTLEGROUND_JOIN_TIMED_OUT
;
1518 // not in the same battleground level bracket, don't let join
1519 PvPDifficultyEntry
const* memberBracketEntry
= GetBattlegroundBracketByLevel(bracketEntry
->mapId
, member
->getLevel());
1520 if(memberBracketEntry
!= bracketEntry
)
1521 return ERR_BATTLEGROUND_JOIN_RANGE_INDEX
;
1522 // don't let join rated matches if the arena team id doesn't match
1523 if(isRated
&& member
->GetArenaTeamId(arenaSlot
) != arenaTeamId
)
1524 return ERR_BATTLEGROUND_JOIN_FAILED
;
1525 // don't let join if someone from the group is already in that bg queue
1526 if(member
->InBattleGroundQueueForBattleGroundQueueType(bgQueueTypeId
))
1527 return ERR_BATTLEGROUND_JOIN_FAILED
; // not blizz-like
1528 // check for deserter debuff in case not arena queue
1529 if(bgOrTemplate
->GetTypeID() != BATTLEGROUND_AA
&& !member
->CanJoinToBattleground())
1530 return ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS
;
1531 // check if member can join any more battleground queues
1532 if(!member
->HasFreeBattleGroundQueueId())
1533 return ERR_BATTLEGROUND_TOO_MANY_QUEUES
; // not blizz-like
1535 return GroupJoinBattlegroundResult(bgOrTemplate
->GetTypeID());
1538 //===================================================
1539 //============== Roll ===============================
1540 //===================================================
1542 void Roll::targetObjectBuildLink()
1544 // called from link()
1545 getTarget()->addLootValidatorRef(this);
1548 void Group::SetDungeonDifficulty(Difficulty difficulty
)
1550 m_dungeonDifficulty
= difficulty
;
1552 CharacterDatabase
.PExecute("UPDATE groups SET difficulty = %u WHERE groupId='%u'", m_dungeonDifficulty
, m_Id
);
1554 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1556 Player
*player
= itr
->getSource();
1557 if(!player
->GetSession() || player
->getLevel() < LEVELREQUIREMENT_HEROIC
)
1559 player
->SetDungeonDifficulty(difficulty
);
1560 player
->SendDungeonDifficulty(true);
1564 void Group::SetRaidDifficulty(Difficulty difficulty
)
1566 m_raidDifficulty
= difficulty
;
1568 CharacterDatabase
.PExecute("UPDATE groups SET raiddifficulty = %u WHERE groupId='%u'", m_raidDifficulty
, m_Id
);
1570 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1572 Player
*player
= itr
->getSource();
1573 if(!player
->GetSession() || player
->getLevel() < LEVELREQUIREMENT_HEROIC
)
1575 player
->SetRaidDifficulty(difficulty
);
1576 player
->SendRaidDifficulty(true);
1580 bool Group::InCombatToInstance(uint32 instanceId
)
1582 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1584 Player
*pPlayer
= itr
->getSource();
1585 if(pPlayer
->getAttackers().size() && pPlayer
->GetInstanceId() == instanceId
)
1591 void Group::ResetInstances(uint8 method
, bool isRaid
, Player
* SendMsgTo
)
1596 // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_DISBAND
1598 // we assume that when the difficulty changes, all instances that can be reset will be
1599 Difficulty diff
= GetDifficulty(isRaid
);
1601 for(BoundInstancesMap::iterator itr
= m_boundInstances
[diff
].begin(); itr
!= m_boundInstances
[diff
].end();)
1603 InstanceSave
*p
= itr
->second
.save
;
1604 const MapEntry
*entry
= sMapStore
.LookupEntry(itr
->first
);
1605 if (!entry
|| entry
->IsRaid() != isRaid
|| (!p
->CanReset() && method
!= INSTANCE_RESET_GROUP_DISBAND
))
1611 if(method
== INSTANCE_RESET_ALL
)
1613 // the "reset all instances" method can only reset normal maps
1614 if (entry
->map_type
== MAP_RAID
|| diff
== DUNGEON_DIFFICULTY_HEROIC
)
1621 bool isEmpty
= true;
1622 // if the map is loaded, reset it
1623 Map
*map
= sMapMgr
.FindMap(p
->GetMapId(), p
->GetInstanceId());
1624 if(map
&& map
->IsDungeon() && !(method
== INSTANCE_RESET_GROUP_DISBAND
&& !p
->CanReset()))
1625 isEmpty
= ((InstanceMap
*)map
)->Reset(method
);
1630 SendMsgTo
->SendResetInstanceSuccess(p
->GetMapId());
1632 SendMsgTo
->SendResetInstanceFailed(0, p
->GetMapId());
1635 if(isEmpty
|| method
== INSTANCE_RESET_GROUP_DISBAND
|| method
== INSTANCE_RESET_CHANGE_DIFFICULTY
)
1637 // do not reset the instance, just unbind if others are permanently bound to it
1641 CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p
->GetInstanceId());
1642 // i don't know for sure if hash_map iterators
1643 m_boundInstances
[diff
].erase(itr
);
1644 itr
= m_boundInstances
[diff
].begin();
1645 // this unloads the instance save unless online players are bound to it
1646 // (eg. permanent binds or GM solo binds)
1647 p
->RemoveGroup(this);
1654 InstanceGroupBind
* Group::GetBoundInstance(Player
* player
)
1656 uint32 mapid
= player
->GetMapId();
1657 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(mapid
);
1661 Difficulty difficulty
= player
->GetDifficulty(mapEntry
->IsRaid());
1663 // some instances only have one difficulty
1664 MapDifficulty
const* mapDiff
= GetMapDifficultyData(mapid
,difficulty
);
1666 difficulty
= DUNGEON_DIFFICULTY_NORMAL
;
1668 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
1669 if(itr
!= m_boundInstances
[difficulty
].end())
1670 return &itr
->second
;
1675 InstanceGroupBind
* Group::GetBoundInstance(Map
* aMap
, Difficulty difficulty
)
1677 // some instances only have one difficulty
1678 MapDifficulty
const* mapDiff
= GetMapDifficultyData(aMap
->GetId(),difficulty
);
1682 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(aMap
->GetId());
1683 if(itr
!= m_boundInstances
[difficulty
].end())
1684 return &itr
->second
;
1689 InstanceGroupBind
* Group::BindToInstance(InstanceSave
*save
, bool permanent
, bool load
)
1691 if(save
&& !isBGGroup())
1693 InstanceGroupBind
& bind
= m_boundInstances
[save
->GetDifficulty()][save
->GetMapId()];
1696 // when a boss is killed or when copying the players's binds to the group
1697 if(permanent
!= bind
.perm
|| save
!= bind
.save
)
1699 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());
1702 CharacterDatabase
.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", GUID_LOPART(GetLeaderGUID()), save
->GetInstanceId(), permanent
);
1704 if(bind
.save
!= save
)
1707 bind
.save
->RemoveGroup(this);
1708 save
->AddGroup(this);
1712 bind
.perm
= permanent
;
1714 DEBUG_LOG("Group::BindToInstance: %d is now bound to map %d, instance %d, difficulty %d", GUID_LOPART(GetLeaderGUID()), save
->GetMapId(), save
->GetInstanceId(), save
->GetDifficulty());
1721 void Group::UnbindInstance(uint32 mapid
, uint8 difficulty
, bool unload
)
1723 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
1724 if(itr
!= m_boundInstances
[difficulty
].end())
1727 CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", GUID_LOPART(GetLeaderGUID()), itr
->second
.save
->GetInstanceId());
1728 itr
->second
.save
->RemoveGroup(this); // save can become invalid
1729 m_boundInstances
[difficulty
].erase(itr
);
1733 void Group::_homebindIfInstance(Player
*player
)
1735 if(player
&& !player
->isGameMaster() && sMapStore
.LookupEntry(player
->GetMapId())->IsDungeon())
1737 // leaving the group in an instance, the homebind timer is started
1738 // unless the player is permanently saved to the instance
1739 InstanceSave
*save
= sInstanceSaveMgr
.GetInstanceSave(player
->GetInstanceId());
1740 InstancePlayerBind
*playerBind
= save
? player
->GetBoundInstance(save
->GetMapId(), save
->GetDifficulty()) : NULL
;
1741 if(!playerBind
|| !playerBind
->perm
)
1742 player
->m_InstanceValid
= false;
1746 static void RewardGroupAtKill_helper(Player
* pGroupGuy
, Unit
* pVictim
, uint32 count
, bool PvP
, float group_rate
, uint32 sum_level
, bool is_dungeon
, Player
* not_gray_member_with_max_level
, Player
* member_with_max_level
, uint32 xp
)
1748 // honor can be in PvP and !PvP (racial leader) cases (for alive)
1749 if (pGroupGuy
->isAlive())
1750 pGroupGuy
->RewardHonor(pVictim
,count
);
1752 // xp and reputation only in !PvP case
1755 float rate
= group_rate
* float(pGroupGuy
->getLevel()) / sum_level
;
1757 // if is in dungeon then all receive full reputation at kill
1758 // rewarded any alive/dead/near_corpse group member
1759 pGroupGuy
->RewardReputation(pVictim
,is_dungeon
? 1.0f
: rate
);
1761 // XP updated only for alive group member
1762 if(pGroupGuy
->isAlive() && not_gray_member_with_max_level
&&
1763 pGroupGuy
->getLevel() <= not_gray_member_with_max_level
->getLevel())
1765 uint32 itr_xp
= (member_with_max_level
== not_gray_member_with_max_level
) ? uint32(xp
*rate
) : uint32((xp
*rate
/2)+1);
1767 pGroupGuy
->GiveXP(itr_xp
, pVictim
);
1768 if(Pet
* pet
= pGroupGuy
->GetPet())
1769 pet
->GivePetXP(itr_xp
/2);
1772 // quest objectives updated only for alive group member or dead but with not released body
1773 if(pGroupGuy
->isAlive()|| !pGroupGuy
->HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GHOST
))
1775 // normal creature (not pet/etc) can be only in !PvP case
1776 if(pVictim
->GetTypeId()==TYPEID_UNIT
)
1777 pGroupGuy
->KilledMonster(((Creature
*)pVictim
)->GetCreatureInfo(), pVictim
->GetObjectGuid());
1782 /** Provide rewards to group members at unit kill
1784 * @param pVictim Killed unit
1785 * @param player_tap Player who tap unit if online, it can be group member or can be not if leaved after tap but before kill target
1787 * Rewards received by group members and player_tap
1789 void Group::RewardGroupAtKill(Unit
* pVictim
, Player
* player_tap
)
1791 bool PvP
= pVictim
->isCharmedOwnedByPlayerOrPlayer();
1793 // prepare data for near group iteration (PvP and !PvP cases)
1797 uint32 sum_level
= 0;
1798 Player
* member_with_max_level
= NULL
;
1799 Player
* not_gray_member_with_max_level
= NULL
;
1801 GetDataForXPAtKill(pVictim
,count
,sum_level
,member_with_max_level
,not_gray_member_with_max_level
,player_tap
);
1803 if(member_with_max_level
)
1805 /// not get Xp in PvP or no not gray players in group
1806 xp
= (PvP
|| !not_gray_member_with_max_level
) ? 0 : MaNGOS::XP::Gain(not_gray_member_with_max_level
, pVictim
);
1808 /// skip in check PvP case (for speed, not used)
1809 bool is_raid
= PvP
? false : sMapStore
.LookupEntry(pVictim
->GetMapId())->IsRaid() && isRaidGroup();
1810 bool is_dungeon
= PvP
? false : sMapStore
.LookupEntry(pVictim
->GetMapId())->IsDungeon();
1811 float group_rate
= MaNGOS::XP::xp_in_group_rate(count
,is_raid
);
1813 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1815 Player
* pGroupGuy
= itr
->getSource();
1819 // will proccessed later
1820 if(pGroupGuy
==player_tap
)
1823 if(!pGroupGuy
->IsAtGroupRewardDistance(pVictim
))
1824 continue; // member (alive or dead) or his corpse at req. distance
1826 RewardGroupAtKill_helper(pGroupGuy
, pVictim
, count
, PvP
, group_rate
, sum_level
, is_dungeon
, not_gray_member_with_max_level
, member_with_max_level
, xp
);
1831 // member (alive or dead) or his corpse at req. distance
1832 if(player_tap
->IsAtGroupRewardDistance(pVictim
))
1833 RewardGroupAtKill_helper(player_tap
, pVictim
, count
, PvP
, group_rate
, sum_level
, is_dungeon
, not_gray_member_with_max_level
, member_with_max_level
, xp
);