diff options
-rw-r--r-- | src/game/Creature.cpp | 35 | ||||
-rw-r--r-- | src/game/GameObject.cpp | 20 | ||||
-rw-r--r-- | src/game/GameObject.h | 3 | ||||
-rw-r--r-- | src/game/Group.cpp | 269 | ||||
-rw-r--r-- | src/game/Group.h | 11 | ||||
-rw-r--r-- | src/game/LootHandler.cpp | 64 | ||||
-rw-r--r-- | src/game/LootMgr.cpp | 118 | ||||
-rw-r--r-- | src/game/LootMgr.h | 22 | ||||
-rw-r--r-- | src/game/Player.cpp | 169 | ||||
-rw-r--r-- | src/game/Unit.cpp | 62 |
10 files changed, 547 insertions, 226 deletions
diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 4b31668db62..9b4332a579f 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -485,7 +485,22 @@ void Creature::Update(uint32 diff) if (m_isDeadByDefault) break; - if( m_deathTimer <= diff ) + if (m_groupLootTimer && lootingGroupLeaderGUID) + { + // for delayed spells + m_Events.Update(diff); + + if (m_groupLootTimer <= diff) + { + Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID); + if (group) + group->EndRoll(&loot); + m_groupLootTimer = 0; + lootingGroupLeaderGUID = 0; + } + else m_groupLootTimer -= diff; + } + else if (m_deathTimer <= diff) { RemoveCorpse(); DEBUG_LOG("Removing corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY)); @@ -493,24 +508,8 @@ void Creature::Update(uint32 diff) else { // for delayed spells - m_Events.Update( diff ); - + m_Events.Update(diff); m_deathTimer -= diff; - if (m_groupLootTimer && lootingGroupLeaderGUID) - { - if(diff <= m_groupLootTimer) - { - m_groupLootTimer -= diff; - } - else - { - Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID); - if (group) - group->EndRoll(); - m_groupLootTimer = 0; - lootingGroupLeaderGUID = 0; - } - } } break; diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index 8f47aa83a2a..aa8e6ca9cec 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -61,6 +61,9 @@ GameObject::GameObject() : WorldObject(), m_goValue(new GameObjectValue) m_DBTableGuid = 0; m_rotation = 0; + m_groupLootTimer = 0; + lootingGroupLeaderGUID = 0; + ResetLootMode(); // restore default loot mode } @@ -210,7 +213,7 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMa return true; } -void GameObject::Update(uint32 /*p_time*/) +void GameObject::Update(uint32 diff) { if (IS_MO_TRANSPORT(GetGUID())) { @@ -432,6 +435,19 @@ void GameObject::Update(uint32 /*p_time*/) m_cooldownTime = 0; } break; + case GAMEOBJECT_TYPE_CHEST: + if (m_groupLootTimer) + { + if (m_groupLootTimer <= diff) + { + Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID); + if (group) + group->EndRoll(&loot); + m_groupLootTimer = 0; + lootingGroupLeaderGUID = 0; + } + else m_groupLootTimer -= diff; + } default: break; } @@ -1111,7 +1127,7 @@ void GameObject::Use(Unit* user) data << GetGUID(); player->GetSession()->SendPacket(&data); } - else if (info->questgiver.gossipID) + else if (info->goober.gossipID) { player->PrepareGossipMenu(this, info->goober.gossipID); player->SendPreparedGossip(this); diff --git a/src/game/GameObject.h b/src/game/GameObject.h index 671bd84addf..1f914a39006 100644 --- a/src/game/GameObject.h +++ b/src/game/GameObject.h @@ -708,6 +708,9 @@ class GameObject : public WorldObject, public GridObject<GameObject> Loot loot; + uint32 m_groupLootTimer; // (msecs)timer used for group loot + uint64 lootingGroupLeaderGUID; // used to find group which is looting + bool hasQuest(uint32 quest_id) const; bool hasInvolvedQuest(uint32 quest_id) const; bool ActivateToQuest(Player *pTarget) const; diff --git a/src/game/Group.cpp b/src/game/Group.cpp index 4fac78bf63f..23fbf1f1010 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -560,16 +560,34 @@ void Group::SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r) } } -void Group::GroupLoot(const uint64& playerGUID, Loot *loot, Creature *creature) +// notify group members which player is the allowed looter for the given creature +void Group::SendLooter(Creature *pCreature, Player *pLooter) +{ + assert(pCreature); + + WorldPacket data(SMSG_LOOT_LIST, (8+8)); + data << uint64(pCreature->GetGUID()); + data << uint8(0); // unk1 + + if (pLooter) + data.append(pLooter->GetPackGUID()); + else + data << uint8(0); + + BroadcastPacket(&data, false); +} + +void Group::GroupLoot(Loot *loot, WorldObject* pLootedObject) { std::vector<LootItem>::iterator i; ItemPrototype const *item; uint8 itemSlot = 0; - Player *player = objmgr.GetPlayer(playerGUID); - Group *group = player->GetGroup(); - for (i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot) + for (i = loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot) { + if (i->freeforall) + continue; + item = objmgr.GetItemPrototype(i->itemid); if (!item) { @@ -578,57 +596,72 @@ void Group::GroupLoot(const uint64& playerGUID, Loot *loot, Creature *creature) } //roll for over-threshold item if it's one-player loot - if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall) + if (item->Quality >= uint32(m_lootThreshold)) { uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM); - Roll* r=new Roll(newitemGUID,*i); + Roll* r = new Roll(newitemGUID,*i); //a vector is filled with only near party members for (GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) { Player *member = itr->getSource(); - if(!member || !member->GetSession()) + if (!member || !member->GetSession()) continue; - if ( i->AllowedForPlayer(member) ) + if (i->AllowedForPlayer(member)) { - if (member->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) + if (member->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) { r->playerVote[member->GetGUID()] = NOT_EMITED_YET; - ++r->totalPlayersRolling; + r->totalPlayersRolling++; } } } - r->setLoot(loot); - r->itemSlot = itemSlot; + if (r->totalPlayersRolling > 0) + { + r->setLoot(loot); + r->itemSlot = itemSlot; - group->SendLootStartRoll(60000, *r); + loot->items[itemSlot].is_blocked = true; - loot->items[itemSlot].is_blocked = true; - creature->m_groupLootTimer = 60000; - creature->lootingGroupLeaderGUID = GetLeaderGUID(); + SendLootStartRoll(60000, *r); - RollId.push_back(r); + RollId.push_back(r); + + if (Creature* creature = dynamic_cast<Creature *>(pLootedObject)) + { + creature->m_groupLootTimer = 60000; + creature->lootingGroupLeaderGUID = GetLeaderGUID(); + } + else if (GameObject* go = dynamic_cast<GameObject *>(pLootedObject)) + { + go->m_groupLootTimer = 60000; + go->lootingGroupLeaderGUID = GetLeaderGUID(); + } + } + else + { + delete r; + } } else i->is_underthreshold=1; - } } -void Group::NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *creature) +void Group::NeedBeforeGreed(Loot *loot, WorldObject* pLootedObject) { ItemPrototype const *item; - Player *player = objmgr.GetPlayer(playerGUID); - Group *group = player->GetGroup(); - uint8 itemSlot = 0; for (std::vector<LootItem>::iterator i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot) { + if (i->freeforall) + continue; + item = objmgr.GetItemPrototype(i->itemid); - //only roll for one-player items, not for ones everyone can get - if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall) + //roll for over-threshold item if it's one-player loot + if (item->Quality >= uint32(m_lootThreshold)) { uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM); Roll* r=new Roll(newitemGUID,*i); @@ -636,15 +669,15 @@ void Group::NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *crea for (GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) { Player *playerToRoll = itr->getSource(); - if(!playerToRoll || !playerToRoll->GetSession()) + if (!playerToRoll || !playerToRoll->GetSession()) continue; - if (playerToRoll->CanUseItem(item) && i->AllowedForPlayer(playerToRoll) ) + if (playerToRoll->CanUseItem(item) && i->AllowedForPlayer(playerToRoll)) { - if (playerToRoll->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) + if (playerToRoll->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) { r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET; - ++r->totalPlayersRolling; + r->totalPlayersRolling++; } } } @@ -654,11 +687,17 @@ void Group::NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *crea r->setLoot(loot); r->itemSlot = itemSlot; - group->SendLootStartRoll(60000, *r); - loot->items[itemSlot].is_blocked = true; + SendLootStartRoll(60000, *r); + RollId.push_back(r); + + if (Creature* creature = dynamic_cast<Creature *>(pLootedObject)) + { + creature->m_groupLootTimer = 60000; + creature->lootingGroupLeaderGUID = GetLeaderGUID(); + } } else { @@ -670,13 +709,9 @@ void Group::NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *crea } } -void Group::MasterLoot(const uint64& playerGUID, Loot* /*loot*/, Creature *creature) +void Group::MasterLoot(Loot* /*loot*/, WorldObject* pLootedObject) { - Player *player = objmgr.GetPlayer(playerGUID); - if(!player) - return; - - sLog.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330) player = [%s].", player->GetName()); + sLog.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330)"); uint32 real_count = 0; @@ -689,7 +724,7 @@ void Group::MasterLoot(const uint64& playerGUID, Loot* /*loot*/, Creature *creat if (!looter->IsInWorld()) continue; - if (looter->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) + if (looter->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) { data << looter->GetGUID(); ++real_count; @@ -701,12 +736,12 @@ void Group::MasterLoot(const uint64& playerGUID, Loot* /*loot*/, Creature *creat for (GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) { Player *looter = itr->getSource(); - if (looter->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) + if (looter->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) looter->GetSession()->SendPacket(&data); } } -void Group::CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 NumberOfPlayers, uint8 Choise) +void Group::CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 NumberOfPlayers, uint8 Choice) { Rolls::iterator rollI = GetRoll(Guid); if (rollI == RollId.end()) @@ -722,37 +757,30 @@ void Group::CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 N if (roll->getLoot()->items.empty()) return; - switch (Choise) + switch (Choice) { case ROLL_PASS: // Player choose pass - { SendLootRoll(0, playerGUID, 128, ROLL_PASS, *roll); ++roll->totalPass; itr->second = PASS; - } - break; + break; case ROLL_NEED: // player choose Need - { SendLootRoll(0, playerGUID, 0, 0, *roll); ++roll->totalNeed; itr->second = NEED; - } - break; + break; case ROLL_GREED: // player choose Greed - { SendLootRoll(0, playerGUID, 128, ROLL_GREED, *roll); ++roll->totalGreed; itr->second = GREED; - } - break; + break; case ROLL_DISENCHANT: // player choose Disenchant - { SendLootRoll(0, playerGUID, 128, ROLL_DISENCHANT, *roll); ++roll->totalGreed; itr->second = DISENCHANT; - } - break; + break; } + if (roll->totalPass + roll->totalNeed + roll->totalGreed >= roll->totalPlayersRolling) { CountTheRoll(rollI, NumberOfPlayers); @@ -760,21 +788,23 @@ void Group::CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 N } //called when roll timer expires -void Group::EndRoll() +void Group::EndRoll(Loot *pLoot) { - Rolls::iterator itr; - while(!RollId.empty()) + for (Rolls::iterator itr = RollId.begin(); itr != RollId.end(); ) { - //need more testing here, if rolls disappear - itr = RollId.begin(); - CountTheRoll(itr, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass + if ((*itr)->getLoot() == pLoot) { + CountTheRoll(itr, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass + itr = RollId.begin(); + } + else + itr++; } } void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) { Roll* roll = *rollI; - if(!roll->isValid()) // is loot already deleted ? + if (!roll->isValid()) // is loot already deleted ? { RollId.erase(rollI); delete roll; @@ -783,7 +813,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) //end of the roll if (roll->totalNeed > 0) { - if(!roll->playerVote.empty()) + if (!roll->playerVote.empty()) { uint8 maxresul = 0; uint64 maxguid = (*roll->playerVote.begin()).first; @@ -794,7 +824,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) if (itr->second != NEED) continue; - uint8 randomN = urand(1, 99); + uint8 randomN = urand(1, 100); SendLootRoll(0, itr->first, randomN, ROLL_NEED, *roll); if (maxresul < randomN) { @@ -805,18 +835,18 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) SendLootRollWon(0, maxguid, maxresul, ROLL_NEED, *roll); player = objmgr.GetPlayer(maxguid); - if(player && player->GetSession()) + if (player && player->GetSession()) { player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT, roll->itemid, maxresul); ItemPosCountVec dest; LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); - if ( msg == EQUIP_ERR_OK ) + uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count); + if (msg == EQUIP_ERR_OK) { item->is_looted = true; roll->getLoot()->NotifyItemRemoved(roll->itemSlot); - --roll->getLoot()->unlootedCount; + roll->getLoot()->unlootedCount--; player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId); } else @@ -829,7 +859,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) } else if (roll->totalGreed > 0) { - if(!roll->playerVote.empty()) + if (!roll->playerVote.empty()) { uint8 maxresul = 0; uint64 maxguid = (*roll->playerVote.begin()).first; @@ -837,12 +867,12 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) RollVote rollvote; Roll::PlayerVote::iterator itr; - for (itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr) + for (itr = roll->playerVote.begin(); itr != roll->playerVote.end(); ++itr) { if (itr->second != GREED && itr->second != DISENCHANT) continue; - uint8 randomN = urand(1, 99); + uint8 randomN = urand(1, 100); SendLootRoll(0, itr->first, randomN, itr->second, *roll); if (maxresul < randomN) { @@ -854,34 +884,34 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) SendLootRollWon(0, maxguid, maxresul, rollvote, *roll); player = objmgr.GetPlayer(maxguid); - if(player && player->GetSession()) + if (player && player->GetSession()) { player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT, roll->itemid, maxresul); LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); - if(rollvote == GREED) + if (rollvote == GREED) { ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); - if ( msg == EQUIP_ERR_OK ) + uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count); + if (msg == EQUIP_ERR_OK) { item->is_looted = true; roll->getLoot()->NotifyItemRemoved(roll->itemSlot); - --roll->getLoot()->unlootedCount; - player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId); + roll->getLoot()->unlootedCount--; + player->StoreNewItem(dest, roll->itemid, true, item->randomPropertyId); } else { item->is_blocked = false; - player->SendEquipError( msg, NULL, NULL ); + player->SendEquipError(msg, NULL, NULL); } } else if(rollvote == DISENCHANT) { item->is_looted = true; roll->getLoot()->NotifyItemRemoved(roll->itemSlot); - --roll->getLoot()->unlootedCount; + roll->getLoot()->unlootedCount--; ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(roll->itemid); player->AutoStoreLoot(pProto->DisenchantID, LootTemplates_Disenchant, true); } @@ -891,8 +921,11 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) else { SendLootAllPassed(NumberOfPlayers, *roll); + + // remove is_blocked so that the item is lootable by all players LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); - if(item) item->is_blocked = false; + if (item) + item->is_blocked = false; } RollId.erase(rollI); delete roll; @@ -1372,7 +1405,18 @@ void Group::ChangeMembersGroup(Player *player, const uint8 &group) } } -void Group::UpdateLooterGuid( Creature* creature, bool ifneed ) +// Retrieve the next Round-Roubin player for the group +// +// No update done if loot method is Master or FFA. +// +// If the RR player is not yet set for the group, the first group member becomes the round-robin player. +// If the RR player is set, the next player in group becomes the round-robin player. +// +// If ifneed is true, +// the current RR player is checked to be near the looted object. +// if yes, no update done. +// if not, he looses his turn. +void Group::UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed) { switch (GetLootMethod()) { @@ -1385,64 +1429,59 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed ) break; } - member_citerator guid_itr = _getMemberCSlot(GetLooterGuid()); - if(guid_itr != m_memberSlots.end()) + uint64 oldLooterGUID = GetLooterGuid(); + member_citerator guid_itr = _getMemberCSlot(oldLooterGUID); + if (guid_itr != m_memberSlots.end()) { - if(ifneed) + if (ifneed) { // not update if only update if need and ok Player* looter = ObjectAccessor::FindPlayer(guid_itr->guid); - if(looter && looter->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) + if (looter && looter->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) return; } ++guid_itr; } // search next after current - if(guid_itr != m_memberSlots.end()) + Player *pNewLooter = NULL; + for (member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr) { - for (member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr) - { - if(Player* pl = ObjectAccessor::FindPlayer(itr->guid)) + if (Player* pl = ObjectAccessor::FindPlayer(itr->guid)) + if (pl->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) { - if (pl->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) + pNewLooter = pl; + break; + } + } + + if (!pNewLooter) + { + // search from start + for (member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr) + { + if (Player* pl = ObjectAccessor::FindPlayer(itr->guid)) + if (pl->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) { - bool refresh = pl->GetLootGUID()==creature->GetGUID(); - - //if(refresh) // update loot for new looter - // pl->GetSession()->DoLootRelease(pl->GetLootGUID()); - SetLooterGuid(pl->GetGUID()); - SendUpdate(); - if(refresh) // update loot for new looter - pl->SendLoot(creature->GetGUID(),LOOT_CORPSE); - return; + pNewLooter = pl; + break; } - } } } - // search from start - for (member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr) + if (pNewLooter) { - if(Player* pl = ObjectAccessor::FindPlayer(itr->guid)) + if (oldLooterGUID != pNewLooter->GetGUID()) { - if (pl->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false)) - { - bool refresh = pl->GetLootGUID()==creature->GetGUID(); - - //if(refresh) // update loot for new looter - // pl->GetSession()->DoLootRelease(pl->GetLootGUID()); - SetLooterGuid(pl->GetGUID()); - SendUpdate(); - if(refresh) // update loot for new looter - pl->SendLoot(creature->GetGUID(),LOOT_CORPSE); - return; - } + SetLooterGuid(pNewLooter->GetGUID()); + SendUpdate(); } } - - SetLooterGuid(0); - SendUpdate(); + else + { + SetLooterGuid(0); + SendUpdate(); + } } uint32 Group::CanJoinBattleGroundQueue(BattleGround const* bgOrTemplate, BattleGroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot) diff --git a/src/game/Group.h b/src/game/Group.h index a28e7a25d1f..a95a09047ca 100644 --- a/src/game/Group.h +++ b/src/game/Group.h @@ -184,7 +184,7 @@ class Group void ChangeLeader(const uint64 &guid); void SetLootMethod(LootMethod method) { m_lootMethod = method; } void SetLooterGuid(const uint64 &guid) { m_looterGuid = guid; } - void UpdateLooterGuid( Creature* creature, bool ifneed = false ); + void UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed = false); void SetLootThreshold(ItemQualities threshold) { m_lootThreshold = threshold; } void Disband(bool hideDestroy=false); @@ -324,9 +324,10 @@ class Group void SendLootRoll(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r); void SendLootRollWon(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r); void SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r); - void GroupLoot(const uint64& playerGUID, Loot *loot, Creature *creature); - void NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *creature); - void MasterLoot(const uint64& playerGUID, Loot *loot, Creature *creature); + void SendLooter(Creature *pCreature, Player *pLooter); + void GroupLoot(Loot *loot, WorldObject* pLootedObject); + void NeedBeforeGreed(Loot *loot, WorldObject* pLootedObject); + void MasterLoot(Loot *loot, WorldObject* pLootedObject); Rolls::iterator GetRoll(uint64 Guid) { Rolls::iterator iter; @@ -341,7 +342,7 @@ class Group } void CountTheRoll(Rolls::iterator roll, uint32 NumberOfPlayers); void CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 NumberOfPlayers, uint8 Choise); - void EndRoll(); + void EndRoll(Loot *loot); void LinkMember(GroupReference *pRef) { m_memberMgr.insertFirst(pRef); } void DelinkMember(GroupReference* /*pRef*/ ) { } diff --git a/src/game/LootHandler.cpp b/src/game/LootHandler.cpp index 9ee41f05fb8..89d5679e751 100644 --- a/src/game/LootHandler.cpp +++ b/src/game/LootHandler.cpp @@ -274,7 +274,7 @@ void WorldSession::HandleLootReleaseOpcode( WorldPacket & recv_data ) DoLootRelease(lguid); } -void WorldSession::DoLootRelease( uint64 lguid ) +void WorldSession::DoLootRelease(uint64 lguid) { Player *player = GetPlayer(); Loot *loot; @@ -284,7 +284,7 @@ void WorldSession::DoLootRelease( uint64 lguid ) player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); - if(!player->IsInWorld()) + if (!player->IsInWorld()) return; if (IS_GAMEOBJECT_GUID(lguid)) @@ -364,13 +364,29 @@ void WorldSession::DoLootRelease( uint64 lguid ) loot->clear(); } else + { // not fully looted object go->SetLootState(GO_ACTIVATED); + + // if the round robin player release, reset it. + if (player->GetGUID() == loot->roundRobinPlayer) + { + if (Group* pGroup = player->GetGroup()) + { + if (pGroup->GetLootMethod() != MASTER_LOOT) + { + loot->roundRobinPlayer = 0; + } + } + else + loot->roundRobinPlayer = 0; + } + } } else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG { Corpse *corpse = ObjectAccessor::GetCorpse(*player, lguid); - if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) + if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) return; loot = &corpse->loot; @@ -384,13 +400,13 @@ void WorldSession::DoLootRelease( uint64 lguid ) else if (IS_ITEM_GUID(lguid)) { Item *pItem = player->GetItemByGuid(lguid ); - if(!pItem) + if (!pItem) return; ItemPrototype const* proto = pItem->GetProto(); // destroy only 5 items from stack in case prospecting and milling - if( (proto->BagFamily & (BAG_FAMILY_MASK_MINING_SUPP|BAG_FAMILY_MASK_HERBS)) && + if ((proto->BagFamily & (BAG_FAMILY_MASK_MINING_SUPP|BAG_FAMILY_MASK_HERBS)) && proto->Class == ITEM_CLASS_TRADE_GOODS) { pItem->m_lootGenerated = false; @@ -399,14 +415,14 @@ void WorldSession::DoLootRelease( uint64 lguid ) uint32 count = pItem->GetCount(); // >=5 checked in spell code, but will work for cheating cases also with removing from another stacks. - if(count > 5) + if (count > 5) count = 5; player->DestroyItemCount(pItem, count, true); } else - // FIXME: item don't must be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or checting possible. - player->DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), true); + // FIXME: item must not be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or cheating possible. + player->DestroyItem(pItem->GetBagSlot(),pItem->GetSlot(), true); return; // item can be looted only single player } else @@ -414,26 +430,40 @@ void WorldSession::DoLootRelease( uint64 lguid ) Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed); - if ( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) + if (!ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) return; loot = &pCreature->loot; - - // update next looter - if(Player *recipient = pCreature->GetLootRecipient()) - if(Group* group = recipient->GetGroup()) - if (group->GetLooterGuid() == player->GetGUID()) - group->UpdateLooterGuid(pCreature); - if (loot->isLooted()) { // skip pickpocketing loot for speed, skinning timer redunction is no-op in fact - if(!pCreature->isAlive()) + if (!pCreature->isAlive()) pCreature->AllLootRemovedFromCorpse(); pCreature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + pCreature->SetLootRecipient(NULL); loot->clear(); } + else + { + // if the round robin player release, reset it. + if (player->GetGUID() == loot->roundRobinPlayer) + { + if (Group* pGroup = player->GetGroup()) + { + if (pGroup->GetLootMethod() != MASTER_LOOT) + { + loot->roundRobinPlayer = 0; + pGroup->SendLooter(pCreature, NULL); + + // force update of dynamic flags, otherwise other group's players still not able to loot. + pCreature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS); + } + } + else + loot->roundRobinPlayer = 0; + } + } } //Player is not looking at loot list, he doesn't need to see updates on the loot list diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index d7e2cde320d..7c4bb2d219c 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -408,11 +408,20 @@ bool Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, // Setting access rights for group loot case Group * pGroup = loot_owner->GetGroup(); - if(!personal && pGroup) + if (!personal && pGroup) { + roundRobinPlayer = loot_owner->GetGUID(); + for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) if(Player* pl = itr->getSource()) FillNotNormalLootFor(pl); + + for (uint8 i = 0; i < items.size(); ++i) + { + if (ItemPrototype const *proto = sItemStorage.LookupEntry<ItemPrototype>(items[i].itemid)) + if (proto->Quality < uint32(pGroup->GetLootThreshold())) + items[i].is_underthreshold = true; + } } // ... for personal loot else @@ -672,6 +681,63 @@ uint32 Loot::GetMaxSlotInLootFor(Player* player) const return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0); } +// return true if there is any FFA, quest or conditional item for the player. +bool Loot::hasItemFor(Player* player) const +{ + QuestItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); + QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(player->GetGUIDLow()); + if (q_itr != lootPlayerQuestItems.end()) + { + QuestItemList *q_list = q_itr->second; + for (QuestItemList::const_iterator qi = q_list->begin() ; qi != q_list->end(); ++qi) + { + const LootItem &item = quest_items[qi->index]; + if (!qi->is_looted && !item.is_looted) + return true; + } + } + + QuestItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); + QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(player->GetGUIDLow()); + if (ffa_itr != lootPlayerFFAItems.end()) + { + QuestItemList *ffa_list = ffa_itr->second; + for (QuestItemList::const_iterator fi = ffa_list->begin() ; fi != ffa_list->end(); ++fi) + { + const LootItem &item = items[fi->index]; + if (!fi->is_looted && !item.is_looted) + return true; + } + } + + QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); + QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(player->GetGUIDLow()); + if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) + { + QuestItemList *conditional_list = nn_itr->second; + for (QuestItemList::const_iterator ci = conditional_list->begin() ; ci != conditional_list->end(); ++ci) + { + const LootItem &item = items[ci->index]; + if (!ci->is_looted && !item.is_looted) + return true; + } + } + + return false; +} + +// return true if there is any item over the group threshold (i.e. not underthreshold). +bool Loot::hasOverThresholdItem() const +{ + for (uint8 i = 0; i < items.size(); ++i) + { + if (!items[i].is_looted && !items[i].is_underthreshold && !items[i].freeforall) + return true; + } + + return false; +} + ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li) { b << uint32(li.itemid); @@ -706,16 +772,46 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) { case GROUP_PERMISSION: { - // You are not the items proprietary, so you can only see + // if you are not the round-robin group looter, you can only see // blocked rolled items and quest items, and !ffa items for (uint8 i = 0; i < l.items.size(); ++i) { if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer)) { - uint8 slot_type = (l.items[i].is_blocked || l.items[i].is_underthreshold) ? 0 : 1; + uint8 slot_type; + + if (l.items[i].is_blocked) + slot_type = LOOT_SLOT_TYPE_ROLL_ONGOING; + else if (l.roundRobinPlayer == 0 || !l.items[i].is_underthreshold || lv.viewer->GetGUID() == l.roundRobinPlayer) + { + // no round robin owner or he has released the loot + // or it IS the round robin group owner + // => item is lootable + slot_type = LOOT_SLOT_TYPE_ALLOW_LOOT; + } + else + // item shall not be displayed. + continue; + + b << uint8(i) << l.items[i]; + b << uint8(slot_type); + ++itemsShown; + } + } + break; + } + case ROUND_ROBIN_PERMISSION: + { + for (uint8 i = 0; i < l.items.size(); ++i) + { + if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer)) + { + if (l.roundRobinPlayer != 0 && lv.viewer->GetGUID() != l.roundRobinPlayer) + // item shall not be displayed. + continue; - b << uint8(i) << l.items[i]; //send the index and the item if it's not looted, and blocked or under threshold, free for all items will be sent later, only one-player loots here - b << uint8(slot_type); // 0 - get 1 - look only + b << uint8(i) << l.items[i]; + b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT); ++itemsShown; } } @@ -724,13 +820,13 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) case ALL_PERMISSION: case MASTER_PERMISSION: { - uint8 slot_type = (lv.permission==MASTER_PERMISSION) ? 2 : 0; + uint8 slot_type = (lv.permission==MASTER_PERMISSION) ? LOOT_SLOT_TYPE_MASTER : LOOT_SLOT_TYPE_ALLOW_LOOT; for (uint8 i = 0; i < l.items.size(); ++i) { if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer)) { - b << uint8(i) << l.items[i]; //only send one-player loot items now, free for all will be sent later - b << uint8(slot_type); // 0 - get 2 - master selection + b << uint8(i) << l.items[i]; + b << uint8(slot_type); ++itemsShown; } } @@ -752,7 +848,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) { b << uint8(l.items.size() + (qi - q_list->begin())); b << item; - b << uint8(0); // allow loot + b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT); ++itemsShown; } } @@ -769,7 +865,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) if (!fi->is_looted && !item.is_looted) { b << uint8(fi->index) << item; - b << uint8(0); // allow loot + b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT); ++itemsShown; } } @@ -786,7 +882,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) if (!ci->is_looted && !item.is_looted) { b << uint8(ci->index) << item; - b << uint8(0); // allow loot + b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT); ++itemsShown; } } diff --git a/src/game/LootMgr.h b/src/game/LootMgr.h index 3db547e0836..2e095fb35ba 100644 --- a/src/game/LootMgr.h +++ b/src/game/LootMgr.h @@ -56,10 +56,11 @@ enum LootMethod enum PermissionTypes { - ALL_PERMISSION = 0, - GROUP_PERMISSION = 1, - MASTER_PERMISSION = 2, - NONE_PERMISSION = 3 + ALL_PERMISSION = 0, + GROUP_PERMISSION = 1, + MASTER_PERMISSION = 2, + ROUND_ROBIN_PERMISSION = 3, + NONE_PERMISSION = 4, }; enum LootType @@ -77,6 +78,15 @@ enum LootType LOOT_INSIGNIA = 21 // unsupported by client, sending LOOT_CORPSE instead }; +// type of Loot Item in Loot View +enum LootSlotType +{ + LOOT_SLOT_TYPE_ALLOW_LOOT = 0, // player can loot the item. + LOOT_SLOT_TYPE_ROLL_ONGOING = 1, // roll is ongoing. player cannot loot. + LOOT_SLOT_TYPE_MASTER = 2, // item can only be distributed by group loot master. + LOOT_SLOT_TYPE_LOCKED = 3, // item is shown in red. player cannot loot. +}; + class Player; class LootStore; @@ -248,6 +258,7 @@ struct Loot std::vector<LootItem> items; uint32 gold; uint8 unlootedCount; + uint64 roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released. LootType loot_type; // required for achievement system Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), loot_type(LOOT_CORPSE) {} @@ -279,6 +290,7 @@ struct Loot quest_items.clear(); gold = 0; unlootedCount = 0; + roundRobinPlayer = 0; i_LootValidatorRefManager.clearReferences(); } @@ -299,6 +311,8 @@ struct Loot LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL); uint32 GetMaxSlotInLootFor(Player* player) const; + bool hasItemFor(Player* player) const; + bool hasOverThresholdItem() const; private: void FillNotNormalLootFor(Player* player); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 69475ff46ac..7305a2ac3f4 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -7917,10 +7917,10 @@ void Player::SendLoot(uint64 guid, LootType loot_type) uint32 lootid = go->GetGOInfo()->GetLootId(); //TODO: fix this big hack - if((go->GetEntry() == BG_AV_OBJECTID_MINE_N || go->GetEntry() == BG_AV_OBJECTID_MINE_S)) - if( BattleGround *bg = GetBattleGround()) - if(bg->GetTypeID() == BATTLEGROUND_AV) - if(!(((BattleGroundAV*)bg)->PlayerCanDoMineQuest(go->GetEntry(),GetTeam()))) + if ((go->GetEntry() == BG_AV_OBJECTID_MINE_N || go->GetEntry() == BG_AV_OBJECTID_MINE_S)) + if (BattleGround *bg = GetBattleGround()) + if (bg->GetTypeID() == BATTLEGROUND_AV) + if (!(((BattleGroundAV*)bg)->PlayerCanDoMineQuest(go->GetEntry(),GetTeam()))) { SendLootRelease(guid); return; @@ -7928,16 +7928,71 @@ void Player::SendLoot(uint64 guid, LootType loot_type) if (lootid) { - sLog.outDebug(" if(lootid)"); loot->clear(); - loot->FillLoot(lootid, LootTemplates_Gameobject, this, false, false, go->GetLootMode()); + + Group* group = GetGroup(); + bool groupRules = (group && go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules); + + // check current RR player and get next if necessary + if (groupRules) + group->UpdateLooterGuid(go, true); + + loot->FillLoot(lootid, LootTemplates_Gameobject, this, !groupRules, false, go->GetLootMode()); + + // get next RR player (for next loot) + if (groupRules) + group->UpdateLooterGuid(go); } if (loot_type == LOOT_FISHING) go->getFishLoot(loot,this); + if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules) + { + if (Group* group = GetGroup()) + { + switch (group->GetLootMethod()) + { + case GROUP_LOOT: + // GroupLoot: rolls items over threshold. Items with quality < threshold, round robin + group->GroupLoot(loot, go); + break; + case NEED_BEFORE_GREED: + group->NeedBeforeGreed(loot, go); + break; + case MASTER_LOOT: + group->MasterLoot(loot, go); + break; + } + } + } + go->SetLootState(GO_ACTIVATED); } + + if (go->getLootState() == GO_ACTIVATED) + { + if (Group* group = GetGroup()) + { + switch (group->GetLootMethod()) + { + case MASTER_LOOT: + permission = MASTER_PERMISSION; + break; + case FREE_FOR_ALL: + permission = ALL_PERMISSION; + break; + case ROUND_ROBIN: + permission = ROUND_ROBIN_PERMISSION; + break; + default: + permission = GROUP_PERMISSION; + break; + } + } + else + permission = ALL_PERMISSION; + } } else if (IS_ITEM_GUID(guid)) { @@ -8019,7 +8074,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) return; } - loot = &creature->loot; + loot = &creature->loot; if (loot_type == LOOT_PICKPOCKETING) { @@ -8029,7 +8084,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) loot->clear(); if (uint32 lootid = creature->GetCreatureInfo()->pickpocketLootId) - loot->FillLoot(lootid, LootTemplates_Pickpocketing, this, false); + loot->FillLoot(lootid, LootTemplates_Pickpocketing, this, true); // Generate extra money for pick pocket loot const uint32 a = urand(0, creature->getLevel()/2); @@ -8047,37 +8102,25 @@ void Player::SendLoot(uint64 guid, LootType loot_type) recipient = this; } - if (creature->lootForPickPocketed) - { - creature->lootForPickPocketed = false; - loot->clear(); - } - if (!creature->lootForBody) { creature->lootForBody = true; - loot->clear(); - - if (uint32 lootid = creature->GetCreatureInfo()->lootid) - loot->FillLoot(lootid, LootTemplates_Creature, recipient, false, false, creature->GetLootMode()); - loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold); + // for creature, loot is filled when creature is killed. if (Group* group = recipient->GetGroup()) { - group->UpdateLooterGuid(creature,true); - switch (group->GetLootMethod()) { case GROUP_LOOT: - // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with quality<threshold, round robin - group->GroupLoot(recipient->GetGUID(), loot, creature); + // GroupLoot: rolls items over threshold. Items with quality < threshold, round robin + group->GroupLoot(loot, creature); break; case NEED_BEFORE_GREED: - group->NeedBeforeGreed(recipient->GetGUID(), loot, creature); + group->NeedBeforeGreed(loot, creature); break; case MASTER_LOOT: - group->MasterLoot(recipient->GetGUID(), loot, creature); + group->MasterLoot(loot, creature); break; default: break; @@ -8089,26 +8132,30 @@ void Player::SendLoot(uint64 guid, LootType loot_type) if (loot_type == LOOT_SKINNING) { loot->clear(); - loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this, false); + loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this, true); } // set group rights only for loot_type != LOOT_SKINNING else { - if(Group* group = GetGroup()) + if (Group* group = GetGroup()) { if (group == recipient->GetGroup()) { - if (group->GetLootMethod() == FREE_FOR_ALL) - permission = ALL_PERMISSION; - else if (group->GetLooterGuid() == GetGUID()) + switch (group->GetLootMethod()) { - if (group->GetLootMethod() == MASTER_LOOT) + case MASTER_LOOT: permission = MASTER_PERMISSION; - else + break; + case FREE_FOR_ALL: permission = ALL_PERMISSION; + break; + case ROUND_ROBIN: + permission = ROUND_ROBIN_PERMISSION; + break; + default: + permission = GROUP_PERMISSION; + break; } - else - permission = GROUP_PERMISSION; } else permission = NONE_PERMISSION; @@ -16077,22 +16124,50 @@ bool Player::isAllowedToLoot(Creature* creature) if (creature->isDead() && !creature->IsDamageEnoughForLootingAndReward()) return false; - if (Player* recipient = creature->GetLootRecipient()) - { - if (recipient == this) - return true; - if (Group* otherGroup = recipient->GetGroup()) - { - Group* thisGroup = GetGroup(); - if (!thisGroup) - return false; - return thisGroup == otherGroup; - } + Loot* loot = &creature->loot; + if (loot->items.size() == 0) return false; - } - else + + Player* recipient = creature->GetLootRecipient(); + if (!recipient) // prevent other players from looting if the recipient got disconnected return !creature->hasLootRecipient(); + + Group* recipientGroup = recipient->GetGroup(); + if (!recipientGroup) + return (this == recipient); + + Group* thisGroup = GetGroup(); + if (!thisGroup || thisGroup != recipientGroup) + return false; + + switch(thisGroup->GetLootMethod()) + { + case FREE_FOR_ALL: + return true; + case ROUND_ROBIN: + case MASTER_LOOT: + // may only loot if the player is the loot roundrobin player + // or if there are free/quest/conditional item for the player + if (loot->roundRobinPlayer == 0 || loot->roundRobinPlayer == GetGUID()) + return true; + + return loot->hasItemFor(this); + case GROUP_LOOT: + case NEED_BEFORE_GREED: + // may only loot if the player is the loot roundrobin player + // or item over threshold (so roll(s) can be launched) + // or if there are free/quest/conditional item for the player + if (loot->roundRobinPlayer == 0 || loot->roundRobinPlayer == GetGUID()) + return true; + + if (loot->hasOverThresholdItem()) + return true; + + return loot->hasItemFor(this); + } + + return false; } void Player::_LoadActions(QueryResult_AutoPtr result, bool startup) diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index a3f7c616154..cd175627a02 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -14560,29 +14560,77 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss) // find player: owner of controlled `this` or `this` itself maybe Player *player = GetCharmerOrOwnerPlayerOrPlayerItself(); + Creature *creature = pVictim->ToCreature(); bool bRewardIsAllowed = true; - if (pVictim->GetTypeId() == TYPEID_UNIT) + if (creature) { - bRewardIsAllowed = pVictim->ToCreature()->IsDamageEnoughForLootingAndReward(); + bRewardIsAllowed = creature->IsDamageEnoughForLootingAndReward(); if (!bRewardIsAllowed) - pVictim->ToCreature()->SetLootRecipient(NULL); + creature->SetLootRecipient(NULL); } - if (bRewardIsAllowed && pVictim->GetTypeId() == TYPEID_UNIT && pVictim->ToCreature()->GetLootRecipient()) - player = pVictim->ToCreature()->GetLootRecipient(); + if (bRewardIsAllowed && creature && creature->GetLootRecipient()) + player = creature->GetLootRecipient(); + // Reward player, his pets, and group/raid members // call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop) - if (bRewardIsAllowed && player && player!=pVictim) + if (bRewardIsAllowed && player && player != pVictim) { WorldPacket data(SMSG_PARTYKILLLOG, (8+8)); //send event PARTY_KILL data << uint64(player->GetGUID()); //player with killing blow data << uint64(pVictim->GetGUID()); //victim - if (Group *group = player->GetGroup()) + + Player* pLooter = player; + + if (Group *group = player->GetGroup()) + { group->BroadcastPacket(&data, group->GetMemberGroup(player->GetGUID())); + + if (creature) + { + group->UpdateLooterGuid(creature, true); + if (group->GetLooterGuid()) + { + pLooter = objmgr.GetPlayer(group->GetLooterGuid()); + if (pLooter) + { + creature->SetLootRecipient(pLooter); // update creature loot recipient to the allowed looter. + group->SendLooter(creature, pLooter); + } + else + group->SendLooter(creature, NULL); + } + else + group->SendLooter(creature, NULL); + + group->UpdateLooterGuid(creature); + } + } else + { player->SendDirectMessage(&data); + WorldPacket data2(SMSG_LOOT_LIST, (8+1+1)); + data2 << uint64(creature->GetGUID()); + data2 << uint8(0); // unk1 + data2 << uint8(0); // no group looter + player->SendMessageToSetInRange(&data2, GetMap()->GetVisibilityDistance(), true); + } + + if (creature) + { + Loot* loot = &creature->loot; + if (creature->lootForPickPocketed) + creature->lootForPickPocketed = false; + + loot->clear(); + if (uint32 lootid = creature->GetCreatureInfo()->lootid) + loot->FillLoot(lootid, LootTemplates_Creature, pLooter, false, false, creature->GetLootMode()); + + loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold); + } + if (player->RewardPlayerAndGroupAtKill(pVictim)) player->ProcDamageAndSpell(pVictim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0); else |