2 * Copyright (C) 2005-2008 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
< TOTAL_DIFFICULTIES
; i
++)
73 for(BoundInstancesMap::iterator itr
= m_boundInstances
[i
].begin(); itr
!= m_boundInstances
[i
].end(); ++itr
)
74 itr
->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_difficulty
= DIFFICULTY_NORMAL
;
98 Player
*leader
= objmgr
.GetPlayer(guid
);
99 if(leader
) m_difficulty
= leader
->GetDifficulty();
101 Player::ConvertInstancesToGroup(leader
, this, guid
);
103 // store group in database
104 CharacterDatabase
.BeginTransaction();
105 CharacterDatabase
.PExecute("DELETE FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid
));
106 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid
));
107 CharacterDatabase
.PExecute("INSERT INTO groups(leaderGuid,mainTank,mainAssistant,lootMethod,looterGuid,lootThreshold,icon1,icon2,icon3,icon4,icon5,icon6,icon7,icon8,isRaid,difficulty) "
108 "VALUES('%u','%u','%u','%u','%u','%u','" I64FMTD
"','" I64FMTD
"','" I64FMTD
"','" I64FMTD
"','" I64FMTD
"','" I64FMTD
"','" I64FMTD
"','" I64FMTD
"','%u','%u')",
109 GUID_LOPART(m_leaderGuid
), GUID_LOPART(m_mainTank
), GUID_LOPART(m_mainAssistant
), uint32(m_lootMethod
),
110 GUID_LOPART(m_looterGuid
), uint32(m_lootThreshold
), m_targetIcons
[0], m_targetIcons
[1], m_targetIcons
[2], m_targetIcons
[3], m_targetIcons
[4], m_targetIcons
[5], m_targetIcons
[6], m_targetIcons
[7], isRaidGroup(), m_difficulty
);
113 if(!AddMember(guid
, name
))
116 if(!isBGGroup()) CharacterDatabase
.CommitTransaction();
121 bool Group::LoadGroupFromDB(const uint64
&leaderGuid
, QueryResult
*result
, bool loadMembers
)
126 bool external
= true;
130 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
131 result
= CharacterDatabase
.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid
));
136 m_leaderGuid
= leaderGuid
;
138 // group leader not exist
139 if(!objmgr
.GetPlayerNameByGUID(m_leaderGuid
, m_leaderName
))
141 if(!external
) delete result
;
145 m_groupType
= (*result
)[13].GetBool() ? GROUPTYPE_RAID
: GROUPTYPE_NORMAL
;
147 if (m_groupType
== GROUPTYPE_RAID
)
148 _initRaidSubGroupsCounter();
150 m_difficulty
= (*result
)[14].GetUInt8();
151 m_mainTank
= (*result
)[0].GetUInt64();
152 m_mainAssistant
= (*result
)[1].GetUInt64();
153 m_lootMethod
= (LootMethod
)(*result
)[2].GetUInt8();
154 m_looterGuid
= MAKE_NEW_GUID((*result
)[3].GetUInt32(), 0, HIGHGUID_PLAYER
);
155 m_lootThreshold
= (ItemQualities
)(*result
)[4].GetUInt16();
157 for(int i
=0; i
<TARGETICONCOUNT
; i
++)
158 m_targetIcons
[i
] = (*result
)[5+i
].GetUInt64();
159 if(!external
) delete result
;
163 result
= CharacterDatabase
.PQuery("SELECT memberGuid, assistant, subgroup FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid
));
169 LoadMemberFromDB((*result
)[0].GetUInt32(), (*result
)[2].GetUInt8(), (*result
)[1].GetBool());
170 } while( result
->NextRow() );
173 if(GetMembersCount() < 2)
180 bool Group::LoadMemberFromDB(uint32 guidLow
, uint8 subgroup
, bool assistant
)
183 member
.guid
= MAKE_NEW_GUID(guidLow
, 0, HIGHGUID_PLAYER
);
185 // skip non-existed member
186 if(!objmgr
.GetPlayerNameByGUID(member
.guid
, member
.name
))
189 member
.group
= subgroup
;
190 member
.assistant
= assistant
;
191 m_memberSlots
.push_back(member
);
193 SubGroupCounterIncrease(subgroup
);
198 void Group::ConvertToRaid()
200 m_groupType
= GROUPTYPE_RAID
;
202 _initRaidSubGroupsCounter();
204 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid
));
208 bool Group::AddInvite(Player
*player
)
210 if(!player
|| player
->GetGroupInvite() || player
->GetGroup())
213 RemoveInvite(player
);
215 m_invitees
.insert(player
->GetGUID());
217 player
->SetGroupInvite(this);
222 bool Group::AddLeaderInvite(Player
*player
)
224 if(!AddInvite(player
))
227 m_leaderGuid
= player
->GetGUID();
228 m_leaderName
= player
->GetName();
232 uint32
Group::RemoveInvite(Player
*player
)
234 for(InvitesList::iterator itr
=m_invitees
.begin(); itr
!=m_invitees
.end(); ++itr
)
236 if((*itr
) == player
->GetGUID())
238 m_invitees
.erase(itr
);
243 player
->SetGroupInvite(NULL
);
244 return GetMembersCount();
247 void Group::RemoveAllInvites()
249 for(InvitesList::iterator itr
=m_invitees
.begin(); itr
!=m_invitees
.end(); ++itr
)
251 Player
*invitee
= objmgr
.GetPlayer(*itr
);
253 invitee
->SetGroupInvite(NULL
);
258 bool Group::AddMember(const uint64
&guid
, const char* name
)
260 if(!_addMember(guid
, name
))
264 Player
*player
= objmgr
.GetPlayer(guid
);
267 if(!IsLeader(player
->GetGUID()) && !isBGGroup())
269 // reset the new member's instances, unless he is currently in one of them
270 // including raid/heroic instances that they are not permanently bound to!
271 player
->ResetInstances(INSTANCE_RESET_GROUP_JOIN
);
273 if(player
->getLevel() >= LEVELREQUIREMENT_HEROIC
&& player
->GetDifficulty() != GetDifficulty() )
275 player
->SetDifficulty(m_difficulty
);
276 player
->SendDungeonDifficulty(true);
279 player
->SetGroupUpdateFlag(GROUP_UPDATE_FULL
);
280 UpdatePlayerOutOfRange(player
);
286 uint32
Group::RemoveMember(const uint64
&guid
, const uint8
&method
)
288 // remove member and change leader (if need) only if strong more 2 members _before_ member remove
289 if(GetMembersCount() > (isBGGroup() ? 1 : 2)) // in BG group case allow 1 members group
291 bool leaderChanged
= _removeMember(guid
);
293 Player
*player
= objmgr
.GetPlayer( guid
);
300 data
.Initialize( SMSG_GROUP_UNINVITE
, 0 );
301 player
->GetSession()->SendPacket( &data
);
304 data
.Initialize(SMSG_GROUP_LIST
, 24);
305 data
<< uint64(0) << uint64(0) << uint64(0);
306 player
->GetSession()->SendPacket(&data
);
308 _homebindIfInstance(player
);
313 WorldPacket
data(SMSG_GROUP_SET_LEADER
, (m_memberSlots
.front().name
.size()+1));
314 data
<< m_memberSlots
.front().name
;
315 BroadcastPacket(&data
);
320 // if group before remove <= 2 disband it
324 return m_memberSlots
.size();
327 void Group::ChangeLeader(const uint64
&guid
)
329 member_citerator slot
= _getMemberCSlot(guid
);
331 if(slot
==m_memberSlots
.end())
336 WorldPacket
data(SMSG_GROUP_SET_LEADER
, slot
->name
.size()+1);
338 BroadcastPacket(&data
);
342 void Group::Disband(bool hideDestroy
)
346 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
348 player
= objmgr
.GetPlayer(citr
->guid
);
352 player
->SetGroup(NULL
);
354 if(!player
->GetSession())
360 data
.Initialize(SMSG_GROUP_DESTROYED
, 0);
361 player
->GetSession()->SendPacket(&data
);
364 data
.Initialize(SMSG_GROUP_LIST
, 24);
365 data
<< uint64(0) << uint64(0) << uint64(0);
366 player
->GetSession()->SendPacket(&data
);
368 _homebindIfInstance(player
);
371 m_memberSlots
.clear();
377 CharacterDatabase
.BeginTransaction();
378 CharacterDatabase
.PExecute("DELETE FROM groups WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid
));
379 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid
));
380 CharacterDatabase
.CommitTransaction();
381 ResetInstances(INSTANCE_RESET_GROUP_DISBAND
, NULL
);
388 /*********************************************************/
389 /*** LOOT SYSTEM ***/
390 /*********************************************************/
392 void Group::SendLootStartRoll(uint32 CountDown
, const Roll
&r
)
394 WorldPacket
data(SMSG_LOOT_START_ROLL
, (8+4+4+4+4+4));
395 data
<< uint64(r
.itemGUID
); // guid of rolled item
396 data
<< uint32(r
.totalPlayersRolling
); // maybe the number of players rolling for it???
397 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
398 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
399 data
<< uint32(r
.itemRandomPropId
); // item random property ID
400 data
<< uint32(CountDown
); // the countdown time to choose "need" or "greed"
402 for (Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
404 Player
*p
= objmgr
.GetPlayer(itr
->first
);
405 if(!p
|| !p
->GetSession())
408 if(itr
->second
!= NOT_VALID
)
409 p
->GetSession()->SendPacket( &data
);
413 void Group::SendLootRoll(const uint64
& SourceGuid
, const uint64
& TargetGuid
, uint8 RollNumber
, uint8 RollType
, const Roll
&r
)
415 WorldPacket
data(SMSG_LOOT_ROLL
, (8+4+8+4+4+4+1+1));
416 data
<< uint64(SourceGuid
); // guid of the item rolled
417 data
<< uint32(0); // unknown, maybe amount of players
418 data
<< uint64(TargetGuid
);
419 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
420 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
421 data
<< uint32(r
.itemRandomPropId
); // Item random property ID
422 data
<< uint8(RollNumber
); // 0: "Need for: [item name]" > 127: "you passed on: [item name]" Roll number
423 data
<< uint8(RollType
); // 0: "Need for: [item name]" 0: "You have selected need for [item name] 1: need roll 2: greed roll
424 data
<< uint8(0); // 2.4.0
426 for( Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
428 Player
*p
= objmgr
.GetPlayer(itr
->first
);
429 if(!p
|| !p
->GetSession())
432 if(itr
->second
!= NOT_VALID
)
433 p
->GetSession()->SendPacket( &data
);
437 void Group::SendLootRollWon(const uint64
& SourceGuid
, const uint64
& TargetGuid
, uint8 RollNumber
, uint8 RollType
, const Roll
&r
)
439 WorldPacket
data(SMSG_LOOT_ROLL_WON
, (8+4+4+4+4+8+1+1));
440 data
<< uint64(SourceGuid
); // guid of the item rolled
441 data
<< uint32(0); // unknown, maybe amount of players
442 data
<< uint32(r
.itemid
); // the itemEntryId for the item that shall be rolled for
443 data
<< uint32(r
.itemRandomSuffix
); // randomSuffix
444 data
<< uint32(r
.itemRandomPropId
); // Item random property
445 data
<< uint64(TargetGuid
); // guid of the player who won.
446 data
<< uint8(RollNumber
); // rollnumber realted to SMSG_LOOT_ROLL
447 data
<< uint8(RollType
); // Rolltype related to SMSG_LOOT_ROLL
449 for( Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
451 Player
*p
= objmgr
.GetPlayer(itr
->first
);
452 if(!p
|| !p
->GetSession())
455 if(itr
->second
!= NOT_VALID
)
456 p
->GetSession()->SendPacket( &data
);
460 void Group::SendLootAllPassed(uint32 NumberOfPlayers
, const Roll
&r
)
462 WorldPacket
data(SMSG_LOOT_ALL_PASSED
, (8+4+4+4+4));
463 data
<< uint64(r
.itemGUID
); // Guid of the item rolled
464 data
<< uint32(NumberOfPlayers
); // The number of players rolling for it???
465 data
<< uint32(r
.itemid
); // The itemEntryId for the item that shall be rolled for
466 data
<< uint32(r
.itemRandomPropId
); // Item random property ID
467 data
<< uint32(r
.itemRandomSuffix
); // Item random suffix ID
469 for( Roll::PlayerVote::const_iterator itr
=r
.playerVote
.begin(); itr
!=r
.playerVote
.end(); ++itr
)
471 Player
*p
= objmgr
.GetPlayer(itr
->first
);
472 if(!p
|| !p
->GetSession())
475 if(itr
->second
!= NOT_VALID
)
476 p
->GetSession()->SendPacket( &data
);
480 void Group::GroupLoot(const uint64
& playerGUID
, Loot
*loot
, Creature
*creature
)
482 std::vector
<LootItem
>::iterator i
;
483 ItemPrototype
const *item
;
485 Player
*player
= objmgr
.GetPlayer(playerGUID
);
486 Group
*group
= player
->GetGroup();
488 for (i
=loot
->items
.begin(); i
!= loot
->items
.end(); ++i
, ++itemSlot
)
490 item
= objmgr
.GetItemPrototype(i
->itemid
);
493 //sLog.outDebug("Group::GroupLoot: missing item prototype for item with id: %d", i->itemid);
497 //roll for over-threshold item if it's one-player loot
498 if (item
->Quality
>= uint32(m_lootThreshold
) && !i
->freeforall
)
500 uint64 newitemGUID
= MAKE_NEW_GUID(objmgr
.GenerateLowGuid(HIGHGUID_ITEM
),0,HIGHGUID_ITEM
);
501 Roll
* r
=new Roll(newitemGUID
,*i
);
503 //a vector is filled with only near party members
504 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
506 Player
*member
= itr
->getSource();
507 if(!member
|| !member
->GetSession())
509 if ( i
->AllowedForPlayer(member
) )
511 if (member
->GetDistance2d(creature
) < sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
))
513 r
->playerVote
[member
->GetGUID()] = NOT_EMITED_YET
;
514 ++r
->totalPlayersRolling
;
520 r
->itemSlot
= itemSlot
;
522 group
->SendLootStartRoll(60000, *r
);
524 loot
->items
[itemSlot
].is_blocked
= true;
525 creature
->m_groupLootTimer
= 60000;
526 creature
->lootingGroupLeaderGUID
= GetLeaderGUID();
531 i
->is_underthreshold
=1;
536 void Group::NeedBeforeGreed(const uint64
& playerGUID
, Loot
*loot
, Creature
*creature
)
538 ItemPrototype
const *item
;
539 Player
*player
= objmgr
.GetPlayer(playerGUID
);
540 Group
*group
= player
->GetGroup();
543 for(std::vector
<LootItem
>::iterator i
=loot
->items
.begin(); i
!= loot
->items
.end(); ++i
, ++itemSlot
)
545 item
= objmgr
.GetItemPrototype(i
->itemid
);
547 //only roll for one-player items, not for ones everyone can get
548 if (item
->Quality
>= uint32(m_lootThreshold
) && !i
->freeforall
)
550 uint64 newitemGUID
= MAKE_NEW_GUID(objmgr
.GenerateLowGuid(HIGHGUID_ITEM
),0,HIGHGUID_ITEM
);
551 Roll
* r
=new Roll(newitemGUID
,*i
);
553 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
555 Player
*playerToRoll
= itr
->getSource();
556 if(!playerToRoll
|| !playerToRoll
->GetSession())
559 if (playerToRoll
->CanUseItem(item
) && i
->AllowedForPlayer(playerToRoll
) )
561 if (playerToRoll
->GetDistance2d(creature
) < sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
))
563 r
->playerVote
[playerToRoll
->GetGUID()] = NOT_EMITED_YET
;
564 ++r
->totalPlayersRolling
;
569 if (r
->totalPlayersRolling
> 0)
572 r
->itemSlot
= itemSlot
;
574 group
->SendLootStartRoll(60000, *r
);
576 loot
->items
[itemSlot
].is_blocked
= true;
586 i
->is_underthreshold
=1;
590 void Group::MasterLoot(const uint64
& playerGUID
, Loot
* /*loot*/, Creature
*creature
)
592 Player
*player
= objmgr
.GetPlayer(playerGUID
);
596 sLog
.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330) player = [%s].", player
->GetName());
598 uint32 real_count
= 0;
600 WorldPacket
data(SMSG_LOOT_MASTER_LIST
, 330);
601 data
<< (uint8
)GetMembersCount();
603 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
605 Player
*looter
= itr
->getSource();
606 if (!looter
->IsInWorld())
609 if (looter
->GetDistance2d(creature
) < sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
))
611 data
<< looter
->GetGUID();
616 data
.put
<uint8
>(0,real_count
);
618 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
620 Player
*looter
= itr
->getSource();
621 if (looter
->GetDistance2d(creature
) < sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
))
622 looter
->GetSession()->SendPacket(&data
);
626 void Group::CountRollVote(const uint64
& playerGUID
, const uint64
& Guid
, uint32 NumberOfPlayers
, uint8 Choise
)
628 Rolls::iterator rollI
= GetRoll(Guid
);
629 if (rollI
== RollId
.end())
633 Roll::PlayerVote::iterator itr
= roll
->playerVote
.find(playerGUID
);
634 // this condition means that player joins to the party after roll begins
635 if (itr
== roll
->playerVote
.end())
639 if (roll
->getLoot()->items
.empty())
644 case 0: //Player choose pass
646 SendLootRoll(0, playerGUID
, 128, 128, *roll
);
651 case 1: //player choose Need
653 SendLootRoll(0, playerGUID
, 0, 0, *roll
);
658 case 2: //player choose Greed
660 SendLootRoll(0, playerGUID
, 128, 2, *roll
);
666 if (roll
->totalPass
+ roll
->totalGreed
+ roll
->totalNeed
>= roll
->totalPlayersRolling
)
668 CountTheRoll(rollI
, NumberOfPlayers
);
672 //called when roll timer expires
673 void Group::EndRoll()
676 while(!RollId
.empty())
678 //need more testing here, if rolls disappear
679 itr
= RollId
.begin();
680 CountTheRoll(itr
, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass
684 void Group::CountTheRoll(Rolls::iterator rollI
, uint32 NumberOfPlayers
)
687 if(!roll
->isValid()) // is loot already deleted ?
694 if (roll
->totalNeed
> 0)
696 if(!roll
->playerVote
.empty())
699 uint64 maxguid
= (*roll
->playerVote
.begin()).first
;
702 for( Roll::PlayerVote::const_iterator itr
=roll
->playerVote
.begin(); itr
!=roll
->playerVote
.end(); ++itr
)
704 if (itr
->second
!= NEED
)
707 uint8 randomN
= urand(1, 99);
708 SendLootRoll(0, itr
->first
, randomN
, 1, *roll
);
709 if (maxresul
< randomN
)
711 maxguid
= itr
->first
;
715 SendLootRollWon(0, maxguid
, maxresul
, 1, *roll
);
716 player
= objmgr
.GetPlayer(maxguid
);
718 if(player
&& player
->GetSession())
720 ItemPosCountVec dest
;
721 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
722 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, roll
->itemid
, item
->count
);
723 if ( msg
== EQUIP_ERR_OK
)
725 item
->is_looted
= true;
726 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
727 --roll
->getLoot()->unlootedCount
;
728 player
->StoreNewItem( dest
, roll
->itemid
, true, item
->randomPropertyId
);
732 item
->is_blocked
= false;
733 player
->SendEquipError( msg
, NULL
, NULL
);
738 else if (roll
->totalGreed
> 0)
740 if(!roll
->playerVote
.empty())
743 uint64 maxguid
= (*roll
->playerVote
.begin()).first
;
746 Roll::PlayerVote::iterator itr
;
747 for (itr
=roll
->playerVote
.begin(); itr
!=roll
->playerVote
.end(); ++itr
)
749 if (itr
->second
!= GREED
)
752 uint8 randomN
= urand(1, 99);
753 SendLootRoll(0, itr
->first
, randomN
, 2, *roll
);
754 if (maxresul
< randomN
)
756 maxguid
= itr
->first
;
760 SendLootRollWon(0, maxguid
, maxresul
, 2, *roll
);
761 player
= objmgr
.GetPlayer(maxguid
);
763 if(player
&& player
->GetSession())
765 ItemPosCountVec dest
;
766 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
767 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, roll
->itemid
, item
->count
);
768 if ( msg
== EQUIP_ERR_OK
)
770 item
->is_looted
= true;
771 roll
->getLoot()->NotifyItemRemoved(roll
->itemSlot
);
772 --roll
->getLoot()->unlootedCount
;
773 player
->StoreNewItem( dest
, roll
->itemid
, true, item
->randomPropertyId
);
777 item
->is_blocked
= false;
778 player
->SendEquipError( msg
, NULL
, NULL
);
785 SendLootAllPassed(NumberOfPlayers
, *roll
);
786 LootItem
*item
= &(roll
->getLoot()->items
[roll
->itemSlot
]);
787 if(item
) item
->is_blocked
= false;
793 void Group::SetTargetIcon(uint8 id
, uint64 guid
)
795 if(id
>= TARGETICONCOUNT
)
800 for(int i
=0; i
<TARGETICONCOUNT
; i
++)
801 if( m_targetIcons
[i
] == guid
)
804 m_targetIcons
[id
] = guid
;
806 WorldPacket
data(MSG_RAID_TARGET_UPDATE
, (2+8));
810 BroadcastPacket(&data
);
813 void Group::GetDataForXPAtKill(Unit
const* victim
, uint32
& count
,uint32
& sum_level
, Player
* & member_with_max_level
, Player
* & not_gray_member_with_max_level
)
815 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
817 Player
* member
= itr
->getSource();
818 if(!member
|| !member
->isAlive()) // only for alive
821 if(!member
->IsAtGroupRewardDistance(victim
)) // at req. distance
825 sum_level
+= member
->getLevel();
826 if(!member_with_max_level
|| member_with_max_level
->getLevel() < member
->getLevel())
827 member_with_max_level
= member
;
829 uint32 gray_level
= MaNGOS::XP::GetGrayLevel(member
->getLevel());
830 if( victim
->getLevel() > gray_level
&& (!not_gray_member_with_max_level
831 || not_gray_member_with_max_level
->getLevel() < member
->getLevel()))
832 not_gray_member_with_max_level
= member
;
836 void Group::SendTargetIconList(WorldSession
*session
)
841 WorldPacket
data(MSG_RAID_TARGET_UPDATE
, (1+TARGETICONCOUNT
*9));
844 for(int i
=0; i
<TARGETICONCOUNT
; i
++)
846 if(m_targetIcons
[i
] == 0)
850 data
<< m_targetIcons
[i
];
853 session
->SendPacket(&data
);
856 void Group::SendUpdate()
860 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
862 player
= objmgr
.GetPlayer(citr
->guid
);
863 if(!player
|| !player
->GetSession())
866 WorldPacket
data(SMSG_GROUP_LIST
, (1+1+1+1+8+4+GetMembersCount()*20));
867 data
<< (uint8
)m_groupType
; // group type
868 data
<< (uint8
)(isBGGroup() ? 1 : 0); // 2.0.x, isBattleGroundGroup?
869 data
<< (uint8
)(citr
->group
); // groupid
870 data
<< (uint8
)(citr
->assistant
?0x01:0); // 0x2 main assist, 0x4 main tank
871 data
<< uint64(0x50000000FFFFFFFELL
); // related to voice chat?
872 data
<< uint32(GetMembersCount()-1);
873 for(member_citerator citr2
= m_memberSlots
.begin(); citr2
!= m_memberSlots
.end(); ++citr2
)
875 if(citr
->guid
== citr2
->guid
)
879 data
<< (uint64
)citr2
->guid
;
881 data
<< (uint8
)(objmgr
.GetPlayer(citr2
->guid
) ? 1 : 0);
882 data
<< (uint8
)(citr2
->group
); // groupid
883 data
<< (uint8
)(citr2
->assistant
?0x01:0); // 0x2 main assist, 0x4 main tank
886 data
<< uint64(m_leaderGuid
); // leader guid
887 if(GetMembersCount()-1)
889 data
<< (uint8
)m_lootMethod
; // loot method
890 data
<< (uint64
)m_looterGuid
; // looter guid
891 data
<< (uint8
)m_lootThreshold
; // loot threshold
892 data
<< (uint8
)m_difficulty
; // Heroic Mod Group
894 player
->GetSession()->SendPacket( &data
);
898 void Group::UpdatePlayerOutOfRange(Player
* pPlayer
)
905 pPlayer
->GetSession()->BuildPartyMemberStatsChangedPacket(pPlayer
, &data
);
907 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
909 player
= itr
->getSource();
910 if (player
&& player
!= pPlayer
&& !pPlayer
->isVisibleFor(player
))
911 player
->GetSession()->SendPacket(&data
);
915 void Group::BroadcastPacket(WorldPacket
*packet
, int group
, uint64 ignore
)
917 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
919 Player
*pl
= itr
->getSource();
920 if(!pl
|| (ignore
!= 0 && pl
->GetGUID() == ignore
))
923 if (pl
->GetSession() && (group
==-1 || itr
->getSubGroup()==group
))
924 pl
->GetSession()->SendPacket(packet
);
928 void Group::BroadcastReadyCheck(WorldPacket
*packet
)
930 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
932 Player
*pl
= itr
->getSource();
933 if(pl
&& pl
->GetSession())
934 if(IsLeader(pl
->GetGUID()) || IsAssistant(pl
->GetGUID()))
935 pl
->GetSession()->SendPacket(packet
);
939 void Group::OfflineReadyCheck()
941 for(member_citerator citr
= m_memberSlots
.begin(); citr
!= m_memberSlots
.end(); ++citr
)
943 Player
*pl
= objmgr
.GetPlayer(citr
->guid
);
944 if (!pl
|| !pl
->GetSession())
946 WorldPacket
data(MSG_RAID_READY_CHECK_CONFIRM
, 9);
949 BroadcastReadyCheck(&data
);
954 bool Group::_addMember(const uint64
&guid
, const char* name
, bool isAssistant
)
956 // get first not-full group
958 if (m_subGroupsCounts
)
960 bool groupFound
= false;
961 for (; groupid
< MAXRAIDSIZE
/MAXGROUPSIZE
; ++groupid
)
963 if (m_subGroupsCounts
[groupid
] < MAXGROUPSIZE
)
969 // We are raid group and no one slot is free
974 return _addMember(guid
, name
, isAssistant
, groupid
);
977 bool Group::_addMember(const uint64
&guid
, const char* name
, bool isAssistant
, uint8 group
)
985 Player
*player
= objmgr
.GetPlayer(guid
);
990 member
.group
= group
;
991 member
.assistant
= isAssistant
;
992 m_memberSlots
.push_back(member
);
994 SubGroupCounterIncrease(group
);
998 player
->SetGroupInvite(NULL
);
999 player
->SetGroup(this, group
);
1000 // if the same group invites the player back, cancel the homebind timer
1001 InstanceGroupBind
*bind
= GetBoundInstance(player
->GetMapId(), player
->GetDifficulty());
1002 if(bind
&& bind
->save
->GetInstanceId() == player
->GetInstanceId())
1003 player
->m_InstanceValid
= true;
1006 if(!isRaidGroup()) // reset targetIcons for non-raid-groups
1008 for(int i
=0; i
<TARGETICONCOUNT
; i
++)
1009 m_targetIcons
[i
] = 0;
1014 // insert into group table
1015 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
);
1021 bool Group::_removeMember(const uint64
&guid
)
1023 Player
*player
= objmgr
.GetPlayer(guid
);
1026 player
->SetGroup(NULL
);
1031 member_witerator slot
= _getMemberWSlot(guid
);
1032 if (slot
!= m_memberSlots
.end())
1034 SubGroupCounterDecrease(slot
->group
);
1036 m_memberSlots
.erase(slot
);
1040 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE memberGuid='%u'", GUID_LOPART(guid
));
1042 if(m_leaderGuid
== guid
) // leader was removed
1044 if(GetMembersCount() > 0)
1045 _setLeader(m_memberSlots
.front().guid
);
1052 void Group::_setLeader(const uint64
&guid
)
1054 member_citerator slot
= _getMemberCSlot(guid
);
1055 if(slot
==m_memberSlots
.end())
1060 // TODO: set a time limit to have this function run rarely cause it can be slow
1061 CharacterDatabase
.BeginTransaction();
1063 // update the group's bound instances when changing leaders
1065 // remove all permanent binds from the group
1066 // in the DB also remove solo binds that will be replaced with permbinds
1067 // from the new leader
1068 CharacterDatabase
.PExecute(
1069 "DELETE FROM group_instance WHERE leaderguid='%u' AND (permanent = 1 OR "
1070 "instance IN (SELECT instance FROM character_instance WHERE guid = '%u')"
1071 ")", GUID_LOPART(m_leaderGuid
), GUID_LOPART(slot
->guid
)
1074 Player
*player
= objmgr
.GetPlayer(slot
->guid
);
1077 for(uint8 i
= 0; i
< TOTAL_DIFFICULTIES
; i
++)
1079 for(BoundInstancesMap::iterator itr
= m_boundInstances
[i
].begin(); itr
!= m_boundInstances
[i
].end();)
1081 if(itr
->second
.perm
)
1083 itr
->second
.save
->RemoveGroup(this);
1084 m_boundInstances
[i
].erase(itr
++);
1092 // update the group's solo binds to the new leader
1093 CharacterDatabase
.PExecute("UPDATE group_instance SET leaderGuid='%u' WHERE leaderGuid = '%u'", GUID_LOPART(slot
->guid
), GUID_LOPART(m_leaderGuid
));
1095 // copy the permanent binds from the new leader to the group
1096 // overwriting the solo binds with permanent ones if necessary
1097 // in the DB those have been deleted already
1098 Player::ConvertInstancesToGroup(player
, this, slot
->guid
);
1100 // update the group leader
1101 CharacterDatabase
.PExecute("UPDATE groups SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot
->guid
), GUID_LOPART(m_leaderGuid
));
1102 CharacterDatabase
.PExecute("UPDATE group_member SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot
->guid
), GUID_LOPART(m_leaderGuid
));
1103 CharacterDatabase
.CommitTransaction();
1106 m_leaderGuid
= slot
->guid
;
1107 m_leaderName
= slot
->name
;
1110 void Group::_removeRolls(const uint64
&guid
)
1112 for (Rolls::iterator it
= RollId
.begin(); it
< RollId
.end(); it
++)
1115 Roll::PlayerVote::iterator itr2
= roll
->playerVote
.find(guid
);
1116 if(itr2
== roll
->playerVote
.end())
1119 if (itr2
->second
== GREED
) --roll
->totalGreed
;
1120 if (itr2
->second
== NEED
) --roll
->totalNeed
;
1121 if (itr2
->second
== PASS
) --roll
->totalPass
;
1122 if (itr2
->second
!= NOT_VALID
) --roll
->totalPlayersRolling
;
1124 roll
->playerVote
.erase(itr2
);
1126 CountRollVote(guid
, roll
->itemGUID
, GetMembersCount()-1, 3);
1130 bool Group::_setMembersGroup(const uint64
&guid
, const uint8
&group
)
1132 member_witerator slot
= _getMemberWSlot(guid
);
1133 if(slot
==m_memberSlots
.end())
1136 slot
->group
= group
;
1138 SubGroupCounterIncrease(group
);
1140 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE group_member SET subgroup='%u' WHERE memberGuid='%u'", group
, GUID_LOPART(guid
));
1145 bool Group::_setAssistantFlag(const uint64
&guid
, const bool &state
)
1147 member_witerator slot
= _getMemberWSlot(guid
);
1148 if(slot
==m_memberSlots
.end())
1151 slot
->assistant
= state
;
1152 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE group_member SET assistant='%u' WHERE memberGuid='%u'", (state
==true)?1:0, GUID_LOPART(guid
));
1156 bool Group::_setMainTank(const uint64
&guid
)
1158 member_citerator slot
= _getMemberCSlot(guid
);
1159 if(slot
==m_memberSlots
.end())
1162 if(m_mainAssistant
== guid
)
1163 _setMainAssistant(0);
1165 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE groups SET mainTank='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainTank
), GUID_LOPART(m_leaderGuid
));
1169 bool Group::_setMainAssistant(const uint64
&guid
)
1171 member_witerator slot
= _getMemberWSlot(guid
);
1172 if(slot
==m_memberSlots
.end())
1175 if(m_mainTank
== guid
)
1177 m_mainAssistant
= guid
;
1178 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE groups SET mainAssistant='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainAssistant
), GUID_LOPART(m_leaderGuid
));
1182 bool Group::SameSubGroup(Player
const* member1
, Player
const* member2
) const
1184 if(!member1
|| !member2
) return false;
1185 if (member1
->GetGroup() != this || member2
->GetGroup() != this) return false;
1186 else return member1
->GetSubGroup() == member2
->GetSubGroup();
1189 // allows setting subgroup for offline members
1190 void Group::ChangeMembersGroup(const uint64
&guid
, const uint8
&group
)
1194 Player
*player
= objmgr
.GetPlayer(guid
);
1199 prevSubGroup
= GetMemberGroup(guid
);
1201 SubGroupCounterDecrease(prevSubGroup
);
1203 if(_setMembersGroup(guid
, group
))
1207 // This methods handles itself groupcounter decrease
1208 ChangeMembersGroup(player
, group
);
1211 // only for online members
1212 void Group::ChangeMembersGroup(Player
*player
, const uint8
&group
)
1214 if(!player
|| !isRaidGroup())
1216 if(_setMembersGroup(player
->GetGUID(), group
))
1219 prevSubGroup
= player
->GetSubGroup();
1221 SubGroupCounterDecrease(prevSubGroup
);
1223 player
->GetGroupRef().setSubGroup(group
);
1228 void Group::UpdateLooterGuid( Creature
* creature
, bool ifneed
)
1230 switch (GetLootMethod())
1236 // round robin style looting applies for all low
1237 // quality items in each loot method except free for all and master loot
1241 member_citerator guid_itr
= _getMemberCSlot(GetLooterGuid());
1242 if(guid_itr
!= m_memberSlots
.end())
1246 // not update if only update if need and ok
1247 Player
* looter
= ObjectAccessor::FindPlayer(guid_itr
->guid
);
1248 if(looter
&& looter
->GetDistance2d(creature
) < sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
))
1254 // search next after current
1255 if(guid_itr
!= m_memberSlots
.end())
1257 for(member_citerator itr
= guid_itr
; itr
!= m_memberSlots
.end(); ++itr
)
1259 if(Player
* pl
= ObjectAccessor::FindPlayer(itr
->guid
))
1261 if (pl
->GetDistance2d(creature
) < sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
))
1263 bool refresh
= pl
->GetLootGUID()==creature
->GetGUID();
1265 //if(refresh) // update loot for new looter
1266 // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1267 SetLooterGuid(pl
->GetGUID());
1269 if(refresh
) // update loot for new looter
1270 pl
->SendLoot(creature
->GetGUID(),LOOT_CORPSE
);
1277 // search from start
1278 for(member_citerator itr
= m_memberSlots
.begin(); itr
!= guid_itr
; ++itr
)
1280 if(Player
* pl
= ObjectAccessor::FindPlayer(itr
->guid
))
1282 if (pl
->GetDistance2d(creature
) < sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
))
1284 bool refresh
= pl
->GetLootGUID()==creature
->GetGUID();
1286 //if(refresh) // update loot for new looter
1287 // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
1288 SetLooterGuid(pl
->GetGUID());
1290 if(refresh
) // update loot for new looter
1291 pl
->SendLoot(creature
->GetGUID(),LOOT_CORPSE
);
1301 //===================================================
1302 //============== Roll ===============================
1303 //===================================================
1305 void Roll::targetObjectBuildLink()
1307 // called from link()
1308 getTarget()->addLootValidatorRef(this);
1311 void Group::SetDifficulty(uint8 difficulty
)
1313 m_difficulty
= difficulty
;
1314 if(!isBGGroup()) CharacterDatabase
.PExecute("UPDATE groups SET difficulty = %u WHERE leaderGuid ='%u'", m_difficulty
, GUID_LOPART(m_leaderGuid
));
1316 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1318 Player
*player
= itr
->getSource();
1319 if(!player
->GetSession() || player
->getLevel() < LEVELREQUIREMENT_HEROIC
)
1321 player
->SetDifficulty(difficulty
);
1322 player
->SendDungeonDifficulty(true);
1326 bool Group::InCombatToInstance(uint32 instanceId
)
1328 for(GroupReference
*itr
= GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
1330 Player
*pPlayer
= itr
->getSource();
1331 if(pPlayer
->getAttackers().size() && pPlayer
->GetInstanceId() == instanceId
)
1337 void Group::ResetInstances(uint8 method
, Player
* SendMsgTo
)
1342 // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_DISBAND
1344 // we assume that when the difficulty changes, all instances that can be reset will be
1345 uint8 dif
= GetDifficulty();
1347 for(BoundInstancesMap::iterator itr
= m_boundInstances
[dif
].begin(); itr
!= m_boundInstances
[dif
].end();)
1349 InstanceSave
*p
= itr
->second
.save
;
1350 const MapEntry
*entry
= sMapStore
.LookupEntry(itr
->first
);
1351 if(!entry
|| (!p
->CanReset() && method
!= INSTANCE_RESET_GROUP_DISBAND
))
1357 if(method
== INSTANCE_RESET_ALL
)
1359 // the "reset all instances" method can only reset normal maps
1360 if(dif
== DIFFICULTY_HEROIC
|| entry
->map_type
== MAP_RAID
)
1367 bool isEmpty
= true;
1368 // if the map is loaded, reset it
1369 Map
*map
= MapManager::Instance().FindMap(p
->GetMapId(), p
->GetInstanceId());
1370 if(map
&& map
->IsDungeon())
1371 isEmpty
= ((InstanceMap
*)map
)->Reset(method
);
1375 if(isEmpty
) SendMsgTo
->SendResetInstanceSuccess(p
->GetMapId());
1376 else SendMsgTo
->SendResetInstanceFailed(0, p
->GetMapId());
1379 if(isEmpty
|| method
== INSTANCE_RESET_GROUP_DISBAND
|| method
== INSTANCE_RESET_CHANGE_DIFFICULTY
)
1381 // do not reset the instance, just unbind if others are permanently bound to it
1382 if(p
->CanReset()) p
->DeleteFromDB();
1383 else CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p
->GetInstanceId());
1384 // i don't know for sure if hash_map iterators
1385 m_boundInstances
[dif
].erase(itr
);
1386 itr
= m_boundInstances
[dif
].begin();
1387 // this unloads the instance save unless online players are bound to it
1388 // (eg. permanent binds or GM solo binds)
1389 p
->RemoveGroup(this);
1396 InstanceGroupBind
* Group::GetBoundInstance(uint32 mapid
, uint8 difficulty
)
1398 // some instances only have one difficulty
1399 const MapEntry
* entry
= sMapStore
.LookupEntry(mapid
);
1400 if(!entry
|| !entry
->SupportsHeroicMode()) difficulty
= DIFFICULTY_NORMAL
;
1402 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
1403 if(itr
!= m_boundInstances
[difficulty
].end())
1404 return &itr
->second
;
1409 InstanceGroupBind
* Group::BindToInstance(InstanceSave
*save
, bool permanent
, bool load
)
1411 if(save
&& !isBGGroup())
1413 InstanceGroupBind
& bind
= m_boundInstances
[save
->GetDifficulty()][save
->GetMapId()];
1416 // when a boss is killed or when copying the players's binds to the group
1417 if(permanent
!= bind
.perm
|| save
!= bind
.save
)
1418 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());
1421 if(!load
) CharacterDatabase
.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", GUID_LOPART(GetLeaderGUID()), save
->GetInstanceId(), permanent
);
1423 if(bind
.save
!= save
)
1425 if(bind
.save
) bind
.save
->RemoveGroup(this);
1426 save
->AddGroup(this);
1430 bind
.perm
= permanent
;
1431 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());
1438 void Group::UnbindInstance(uint32 mapid
, uint8 difficulty
, bool unload
)
1440 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
1441 if(itr
!= m_boundInstances
[difficulty
].end())
1443 if(!unload
) CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", GUID_LOPART(GetLeaderGUID()), itr
->second
.save
->GetInstanceId());
1444 itr
->second
.save
->RemoveGroup(this); // save can become invalid
1445 m_boundInstances
[difficulty
].erase(itr
);
1449 void Group::_homebindIfInstance(Player
*player
)
1451 if(player
&& !player
->isGameMaster() && sMapStore
.LookupEntry(player
->GetMapId())->IsDungeon())
1453 // leaving the group in an instance, the homebind timer is started
1454 // unless the player is permanently saved to the instance
1455 InstanceSave
*save
= sInstanceSaveManager
.GetInstanceSave(player
->GetInstanceId());
1456 InstancePlayerBind
*playerBind
= save
? player
->GetBoundInstance(save
->GetMapId(), save
->GetDifficulty()) : NULL
;
1457 if(!playerBind
|| !playerBind
->perm
)
1458 player
->m_InstanceValid
= false;